Reputation: 1429
Suppose you have a template argument T
.
What are the differences between
add_cv_t<T>
and const volatile T
add_const_t<T>
and const T
add_volatile_t<T>
and volatile T
add_lvalue_reference_t<T>
and T&
add_rvalue_reference_t<T>
and T&&
add_pointer_t<T>
and T*
?Why should I use add_rvalue_reference_t<T>
instead of T&&
for example. Are there any rules when to choose which?
Upvotes: 10
Views: 754
Reputation: 8576
In most cases, std::add_rvalue_reference_t<T>
is equivalent to T&&
. However, reference collapsing rules and the-rules-that-dictate-which-types-are-referenceable may have your code misbehave if not taked into account.
There are, however, some cases where the type
static member type will be different due to T
being a non-referenceable type. For example std::add_rvalue_reference_t<void>
resolves to void
, and (taking another template you mentioned as an example) std::add_pointer_t<T&>
resolves to T*
(if you want to invoke chaos, the required ritual is std::add_pointer_t<std::add_rvalue_reference_t<void>>
:))
Respecting to uses, it may be used as a template template parameter to do some funky black magic. Anyway, stuff like std::is_rvalue_reference_t<T>
or std::remove_reference_t<T>
is usually more commonly used when manipulating the type's reference attributes.
Upvotes: 0
Reputation: 1827
According to what I see in STL source:
add_cv_t<T>
and const volatile T
- no difference
add_const_t<T>
and const T
- no difference
add_volatile_t<T>
and volatile T
- no difference
add_lvalue_reference_t<T>
and T&
- there is difference for example if T is non referenceable type void. add_lvalue_reference_t<void>::type = void
and void&
= compile-time error
add_rvalue_reference_t<T>
and T&&
- the same as above
add_pointer_t<T>
and T*
- difference when T is reference, because there is no such thing as pointer to reference. add_pointer_t<T>
is equivalent to std::remove_reference<T>::type*
Upvotes: 4
Reputation: 119467
add_cv_t<T>
andconst volatile T
add_const_t<T>
andconst T
add_volatile_t<T>
andvolatile T
No difference; the definition of add_const<T>::type
is just T const
, for example.
add_lvalue_reference_t<T>
andT&
add_rvalue_reference_t<T>
andT&&
T&
and T&&
are ill-formed when T
is cv void
, but these templates are well-formed, just giving the original type back.
add_pointer_t<T>
andT*
?
add_pointer_t<T>
is equivalent to std::remove_reference<T>::type*
. That is, if T
is a reference type, it gives a pointer to the referenced type. On the other hand, T*
will be ill-formed since you cannot have a pointer to a reference.
Which should you use?
T
. Of course, that means that if you want deduction, you should avoid them.T*
are useful in generic code since they "do the right thing". For example, if T
is deduced from an argument of type T&&
, then T*
does the wrong thing when the argument is an lvalue, since it tries to declare a pointer to an lvalue reference. But std::add_pointer_t<T>
will give a pointer to the actual type of the argument.Upvotes: 14