- Those used in template type deduction.
- Those used in
auto
type deduction. - Those used by
decltype
.
auto
type deduction are the same as the rules for template type deduction, except that given a braced initializer such as { 1, 2, 3, 4 }
, auto
will deduce a std::initializer
list type (in the case of { 1, 2, 3, 4 }
, it will be std::initializer_list<int>
), while template type deduction will fail. (I have no idea why type deduction for auto
and for templates is not identical. If you know, please tell me!) The rules for decltype
are more complicated, because they don't just distinguish between lvalues and rvalues, they also distinguish between id-expressions (i.e., expressions consisting only of identifiers, e.g., variable or parameter names) and non-id-expressions. For details on all these rules, consult this article by Thomas Becker, this article by me, or this article by Herb Sutter (for auto
) and this one by Andrew Koenig (for decltype
).But that's for C++11, which, among the C++-obsessed, is rapidly approaching yawnworthiness. Fortunately, C++14 is on the horizon, and one of the new features sure to stifle even the strongest of yawns is the ability to declare types using
decltype(auto)
. This feature leads to two questions, only the first of which is rhetorical:- You can declare what?
- During type deduction for
decltype(auto)
, which type deduction rules are to be followed: those forauto
or those fordecltype
? Or doesdecltype(auto)
have its own set of type deduction rules?
decltype(auto)
uses the decltype
type deduction rules. The reason is that the type deduced by auto
for an initializing expression strips the ref-qualifiers (i.e., lvalue references and rvalue references) and top-level cv-qualifiers (i.e., const
s and volatile
s) from the expression, but decltype
does not. As a result, if you want the ref- and cv-qualifier stripping behavior, you can just write auto
. If you don't, C++14 gives you the option of writing decltype(auto)
.For variable declarations, this saves you the trouble of typing the initializing expression twice,
decltype(longAndComplexInitializingExpression) var = longAndComplexInitializingExpression; // C++11 decltype(auto) var = longAndComplexInitializingExpression; // C++14For
auto
function return types (another new C++14 feature), it's even more convenient. Consider a function template, grab
, that authenticates a user and, assuming authentication doesn't throw, returns the result of indexing into some container-like object. Bearing in mind that some standard containers return lvalue references from their operator[]
operations (e.g., std::vector
, std::deque
), while others return proxy objects (e.g., std::vector<bool>
), and I believe it would be valid to define a container such that invoking operator[]
on an rvalue would yield an rvalue reference, the proper "generic" way to declare this function in C++11 would be (I think):template<typename ContainerType, typename IndexType> //C++11 auto grab(ContainerType&& container, IndexType&& index) -> decltype(std::forward<ContainerType>(container)[std::forward<IndexType>(index)]); { authenticateUser(); return std::forward<ContainerType>(container)[std::forward<IndexType>(index)]; }In C++14, I believe this can be simplified to the following, thanks to function return type deduction and
decltype(auto)
:
template<typename ContainerType, typename IndexType> // C++14 decltype(auto) grab(ContainerType&& container, IndexType&& index) { authenticateUser(); return std::forward<ContainerType>(container)[std::forward<IndexType>(index)]; }Scott