I think the book contains an error on page 711:
In § 16.2.3 (p. 684) we introduced the library
remove_reference
type. That template works through a series of specializations:// original, most general template template <class T> struct remove_reference { typedef T type; }; // partial specializations that will be used fore lvalue and rvalue references template <class T> struct remove_reference<T&> // lvalue references typedef T type; }; template <class T> struct remove_reference<T&&> // rvalue references typedef T type; };
...
int i; // declyptype(42) is int, used the original template remove_reference<decltype(42)>::type a; // decltype(i) is int&, uses first(T&) partial specialization remove_reference<decltype(i)>::type b; // delctype(std::move(i)) is int&&, uses second (i.e., T&&) partial specialization remove_reference<decltype(std::move(i))>::type c;
All three variables,
a
,b
,andc
, have typeint
.
I think decltype(i)
yields a plain int
and not int&
therefore the most general template is used actually in case of b
. For plain variable types [1] the decltype
type specifier yields plain types and for other expressions which can be used as lvalue
the it will yield and lvalue reference
.
Example
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
template <typename T> struct blubb {
typedef T type;
void print() { cout << "plain argument type\n"; }
};
template <typename T> struct blubb<T&> {
typedef T type;
void print() { cout << "lvalue reference type\n"; }
};
template <typename T> struct blubb<T&&> {
typedef T type;
void print() { cout << "rvalue reference type\n"; }
};
int main() {
int i = 0;
blubb<decltype(42)> plain;
plain.print();
blubb<decltype(i)> lvalue_ref; // actually not!
lvalue_ref.print();
int* pi = &i;
blubb<decltype(*pi)> lvalue_ref2; // expression which can be on the left hand side
lvalue_ref2.print();
blubb<decltype(std::move(i))> rvalue_ref;
rvalue_ref.print();
return 0;
}
Compile and Run
g++ -o types types.cpp -Wall -pedantic -g && ./types
plain argument type
plain argument type
lvalue reference type
rvalue reference type
Please tell me if I'm right or wrong and explain if appropriate.
Thank you
[1] Probably the correct term is id-expression
?
Yes, for unparenthesized id-expression, decltype
yields the type of entity named by the id-expression, then decltype(i)
yields the type int
.
- If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then
decltype
yields the type of the entity named by this expression.
On the other hand, decltype((i))
yields the type int&
; (i)
is treated as an lvalue expression.
- If the argument is any other expression of type
T
, and
b) if the value category of expression is lvalue, then decltype yieldsT&
;
Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus
decltype(x)
anddecltype((x))
are often different types.