/* */ /* */

Saturday, July 19, 2014

Free Excerpt from Draft EMC++ Now Available

O'Reilly has made the TOC, Introduction, and first chapter ("Deducing Types") from the draft version of Effective Modern C++ available for free download. That's roughly the first 40 pages of the book, including Items 1-4. The PDF is available here.

I hope you enjoy this sample content, but I can't resist reminding you that this is from a draft manuscript. The final version will be better.

As always, I welcome suggestions for how this material can be improved.

Scott

25 comments:

Matt said...

Looks quite nice & informative so far!

Compiler Diagnostics in Item 4 seems like a handy technique.

Perhaps it would also make sense to mention Boost.TypeIndex (as of Boost 1.56.0, in beta 1 at the moment of writing) as potentially useful in this context (especially given the issues with typeid and std::type_info::name -- seems to fit in the "Beyond typeid" topic).

More information:
http://www.boost.org/users/history/version_1_56_0.html
http://www.boost.org/doc/libs/1_56_0_b1/doc/html/boost_typeindex.html

In particular, these two advantages (relative to disadvantages of typeid and std::type_info::name) seem relevant:
- some implementations of typeid(T) erroneously do not strip const, volatile and references from type
- no nice and portable way to get human readable type names.

Matt said...

Another thing possibly worth a mention is the following change coming in C++17:
"auto var{expr}; is now valid and equivalent to T var{expr}; (where T is the deduced type)"

Source: http://botondballo.wordpress.com/2014/07/17/trip-report-c-standards-committee-meeting-in-rapperswil-june-2014/

Scott Meyers said...

@Matt: Boost.TypeIndex looks interesting (thanks for the pointer), but I find that it gives the same incorrect type information for the parameter param in the "f(&vw[0])" example in Item 4. I'm using boost::typeindex::type_id_runtime(param).pretty_name(). As far as you know, am I doing something wrong? I'd be happy to mention Boost.TypeIndex if it produced accurate information.

As for C++17 developments, my plan is to not mention them in the book. A lot can happen between now and 2017, and things have been added to and then removed from draft standards before (e.g., concepts, dynarray, and optional). I frankly hope that the committee doesn't change the meaning of "auto var {expr}", because that will break C++11 code, including code I'll be showing in the book to work around the inability to perfect-forward braced parameter lists.

nosenseetal said...

Nice, bought the book, what I read looks nice, but tbh I feel free sample is a bad pick because it deals with the boring parts and more importantly does not give me an effective feeling like for eg lambdas over bind part...


Also does anybody know how can I buy and send this book as a gift to a friend? Cant find a way to do it on Oreilly page.

Scott Meyers said...

@nosenseetal: Yes, I agree, it would probably have been better to choose the chapter on lambdas as a sample, because the chapter on type deduction isn't really representative of the most useful information in the book. That's my fault, because I suggested the sample material to O'Reilly :-(

nosenseetal said...

Well good that I did not suggest to fire person that made the chapter choice ;)


And again if somebody knows how can I gift this book to a friend please reply.
World needs more Modern C++. :)

Philipp Stephani said...

The chapter uses a different definition of "type of an expression" than the standard: in the standard, expressions never have reference type, so statements like "If expr’s type is a reference, ignore the reference part" would not be applicable. However, your definition is probably more common and less confusing (people would intuitively say that the type of a function call expression is the same as the return type of the called function, but the standard disagrees). Maybe a note could be added about the different definitions?

Scott Meyers said...

@philippe Stephani: I've had a number of run-ins with language lawyers who argue that expressions never have reference types. I don't know where this idea comes from, because it's demonstratively false. Variable names are expressions, and the type of a variable that's declared to be a reference is a reference type. For example, 12.8/11 refers to "data member[s] of rvalue reference type." (This is independent of the behavior for decltype, by the way, which also returns reference types for variables declared to be references.) And 5/5 says "If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis." There would be no need to adjust the type of an expression of reference type if there were no expressions of reference type.

With that off my chest, what part of my chapter do you think contradicts the standard?

Philipp Stephani said...

I guess 5/5 is the main source for this, but interpreted in such a way that the "type of an expression" always refers to the "adjusted type" instead of the "initial type". For example, http://en.cppreference.com/mwiki/index.php?title=cpp/language/value_category&oldid=71932 says "Each expression has some non-reference type".

That said, I found a few places of the standard where I think the wording is correct only if it is assumed that the term "type of an expression" always refers to the adjusted type:

§ 14.8.2.1/3 (function template parameter deduction) talks about adjustments to the parameter type in case it is a reference type, but not about any adjustments to the argument type, suggesting it can't be a reference type.

§ 7.1.6.2/4 (decltype) talks only about adding references to the type, and it seems reference collapsing doesn't apply here, so the expression type can't be a reference.

5.2.8/3 (typeid for expressions) says nothing about ignoring references, but /5 explains that consts are ignored, so references need to have been ignored elsewhere (as a result of 5/5).

Also http://stackoverflow.com/a/17242295/178761: "...the type of an evaluated expression is never a reference type..."
This seems to suggest that at least some people use the term "type of an expression" for the "adjusted type".

I don't know of a way to query this (adjusted) type of an expression directly. I guess typeid(void(std::remove_reference(decltype((expr))):type)) should do it (using a function taking an argument to work around stripping top-level cv and references). I'm also not aware of any direct use of this term that doesn't require further adjustments to the type (such as adding or removing const or references), so I guess it's mostly a purely editorial tool to simplify the wording of a few definitions a bit. Unfortunately it still causes some confusion.

Scott Meyers said...

@Philipp Stephani: I think even the notion of an adjusted type may be ambiguous, because it includes what I thought was normally referred to as array-to-pointer and function-to-pointer "decay".

At any rate, my goal in my book is to explain how things work using terminology normal developers can understand, and if you ask normal developers what the type of x is given

int&& x = 10;

they'll all say it's int&&. And they'll be correct. Any other answer will run contrary to the results of decltype(x) as well as the reference-initialization rules.

That being the case, it makes no sense to say that using x like this,

int y = x;

causes its type to magically change to int. x's type is still int&&. 5/5 turns that into int, but that doesn't change what x's type truly is.

Scott Meyers said...

@nosenseetal: I asked my editor today about buying the book as a gift for somebody else. She says it's not possible, sorry, and she agrees that the limitation makes no sense. (She also agrees that it makes no sense that it's not possible to preorder the digital and print books at the usual print+digital bundle price.)

I'm sorry I can't offer better news.

Matt said...

@Scott: Yes, by default `boost::typeindex::type_info` is a drop-in replacement for `std::type_info` -- which means that it has to be compatible with the same mandated requiremetns (including the quirks :]).

However, the library also offers `boost::typeindex::type_id_with_cvr<T>()` (where `cvr` stands for const, volatile, reference) which preserves the exact type:
http://www.boost.org/doc/libs/1_56_0_b1/doc/html/boost/typeindex/type_id_with_cvr.html

We can even store this exact type -- here's an example:
http://www.boost.org/doc/libs/1_56_0_b1/doc/html/boost_typeindex/examples.html#boost_typeindex.examples.exact_type_matching_storing_type

In the context of Item 4, here's how we can use it with `T` as well as `decltype(param)`, with the following results:

clang 3.4.2
boost::typeindex::type_id_with_cvr<T>().pretty_name() = Widget const*
boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() = Widget const* const&

g++ 4.9.0
boost::typeindex::type_id_with_cvr<T>().pretty_name() = Widget const*
boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() = Widget const* const&

MSVC 2013 (Win32)
boost::typeindex::type_id_with_cvr<T>().pretty_name() = struct Widget const *
boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() = struct Widget const * const &

MSVC 2013 (x64)
boost::typeindex::type_id_with_cvr<T>().pretty_name() = struct Widget const * __ptr64
boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() = struct Widget const * __ptr64 const & __ptr64

HTH :-)

// Fair enough on C++17; it's interesting that this proposal could break the code, now I _really_ wish my subscription covered "Rough Cuts"...

Scott Meyers said...

@Matt: Thanks for showing me the proper use of Boost.TypeIndex. I now plan to mention this in what's currently Item 4.A cross-platform mechanism for getting accurate type information at runtime is sweet.

Hubert Schmid said...

Hi Scott,
another comment regarding Compiler Diagnostics in Item 4:
Instead of using a class template declaration without definition, it might be worthwhile thinking about using a deleted function. IMO it makes the intention more explicit. Here is an example:

template <typename ...Types, typename ...Args>
void deleted(Args&&...) = delete;


You can use it with an arbitrary number of types, arguments and a combination of both. GCC and Clang show you the exact types when trying to use the function. Never tested it with VC++.

deleted<int&, decltype(4.2)>(+'x');

Regards, Hubert

Scott Meyers said...

@Hubert Schmid: Interesting suggestion, thanks. I'll check it out.

Scott Meyers said...

@Hubert Schmid: I must be missing something, because I'm not able to make your idea work. Given this code,

const int theAnswer = 42;
auto x = theAnswer;

deleted<decltype(x)>();

gcc 4.8 says:

typedisplay.cpp: In function 'int main()':
typedisplay.cpp:12:24: error: use of deleted function 'void deleted(Args &&
...
)'
deleted<decltype(x)>();
^
typedisplay.cpp:4:6: error: declared here
void deleted(Args &&...) = delete;

Note that the type of x is never mentioned.

What am I doing wrong?

Scott Meyers said...

@Matt: I'd like to add you to the book's acknowledgments for telling me about Boost.TypeIndex. Can you please email me and let me know how you'd like your name to be listed? My email is smeyers@aristeia.com.

Thanks.

Scott Meyers said...

Hey, if anybody out there has access to clang with the most recent Boost, can you please compile and run this program and let me know what the output is? Thanks!

#include <iostream>
#include <vector>
#include <boost/type_index.hpp>

template<typename T>
void f(const T& param)
{
using std::cout;
using boost::typeindex::type_id_with_cvr;
// show T
cout << "T = "
<< type_id_with_cvr<T>().pretty_name()
<< '\n';
// show param's type
cout << "param = "
<< type_id_with_cvr<decltype(param)>().pretty_name()
<< '\n';

}

class Widget {};

std::vector<Widget> createVec()
{
return std::vector<Widget>(5);
}

int main()
{
const auto vw = createVec();
if (!vw.empty()) {
f(&vw[0]);
}
}

Hubert Schmid said...

@Scott: I have tested it again with GCC-4.8 and GCC-4.9. Both compilers produce the same error message (except for color support in 4.9). The types are shown after the function template signature.

test.cpp: In function ‘int main()’:
test.cpp:8:26: error: use of deleted function ‘void deleted(Args&& ...) [with Types = {int}; Args = {}]
deleted<decltype(x)>();
^
test.cpp:2:6: note: declared here
void deleted(Args&&...) = delete;
^

Clang 3.5 shows the following diagnostic message:

test.cpp:8:5: error: call to deleted function 'deleted'
deleted<decltype(x)>();
^~~~~~~~~~~~~~~~~~~~
test.cpp:2:6: note: candidate function [with Types = <int>, Args = <>] has been explicitly deleted
void deleted(Args&&...) = delete;
^
1 error generated.

Regards, Hubert

Scott Meyers said...

@Hubert Schmid: Okay, now I'm seeing what you're seeing. Sorry--pilot error on my part.

However, I'm inclined to stick with my undefined template, because I find the parens needed to call the deleted function unnecessary noise, and I like the ability to declare a variable name that helps make the diagnostic clearer. But your deleted approach clearly works.

Caibbor said...

I've been reading the draft and came to the part about C++ having no terminology to distinguish between an object that is a copy of another that is created from a copy ctor from one that is created from a move ctor.

Well, if one is a copy, may I suggest that we call the other a movy? :)

Scott Meyers said...

@Caibbor: I considered introducing the term "move-copy" (e.g., "object A is a move-copy of object B"), but I decided to limit my vocabulary invention to "universal reference."

If you'd like to try to get "movy" adopted as standard parlance, I say go for it :-)

Niels Dekker said...

FWIW, I got a curious result from the Type Displayer (TD) from item 4, "Know how to view deduced types". Visual C++ produces two compile errors, instead of one, on the following code:

   //////////////////////////////////////////////////
   template < typename T > class TD; // Type Displayer

   int main(int argc, char** argv)
   {
      int& x = argc;
      [=]
      {
         TD<decltype(x)> xType;
      };
   }
   //////////////////////////////////////////////////

Compiler output:

Microsoft (R) C/C++ Optimizing Compiler Version 18.00.30723 for x86
...
TD.cpp(8) : error C2079: 'xType' uses undefined class 'TD<int &>'
TD.cpp(8) : error C2079: 'xType' uses undefined class 'TD<int>'


As can be tested online at http://rextester.com/YNJ24014

So it looks like in this case, decltype(x) yields two types instead of one! But in fact, it's just a compiler bug: "[C++] decltype(r) goes wrong inside a lambda, when 'r' is a reference declared outside the lambda", https://connect.microsoft.com/VisualStudio/feedbackdetail/view/846973

Matt said...

@Scott: Just send you an e-mail, thanks!

BTW, about Clang on Windows -- I'm using MSYS2, which has a package manager (`pacman`) that gives access to Clang.
URL: http://sourceforge.net/projects/msys2/

// This is what I've used to produce the example output in the blog comment :-)

Here are the instructions: http://sourceforge.net/p/msys2/wiki/MSYS2%20installation/
// Apparently, there's also an installer, but I haven't used it: http://msys2.github.io/

Scott Meyers said...

@Matt: I replied to your email, thanks.