Here be dragons.
logger(logger const& other) : value_(other.value_)
The expression other.value_
is an lvalue of type T const
, e.g. int&
, int&&
or int const
.
If
T == int&&
, you need to do amove
, as the expression is an lvalue. Themove
is equivalent to astatic_cast<int&&>
, so you could do thestatic_cast
directly as well.If
T == int&
, no cast is required.If
T == int
, no cast is required.
For a copy ctor defined as:
logger(logger const& other) : value_(static_cast<T>(other.value_)) {/*...*/}
Applied to the third case, this is defined as the introduction of a temporary, and could result in an additional copy/move, although I think it can&will be elided.
A solution without relying on the copy/move elision is to introduce a weird_cast
, that yields the desired type in any case:
#include <type_traits>
template<class T, class U>
typename std::enable_if<std::is_reference<T>{}, T>::type
weird_cast(U& p)
{
return static_cast<T>(p);
}
template<class T, class U>
typename std::enable_if<not std::is_reference<T>{}, T const&>::type
weird_cast(U const& p)
{
return p;
}
int main()
{
int o = 42;
int & lo = o;
int && ro = std::move(o);
int const lco = o;
int&& r = weird_cast<int&&>(ro);
int& l = weird_cast<int& >(lo);
int d = weird_cast<int >(lco);
}
This is similar to std::forward
, but also supports "forwarding" non-reference types.
Where are the dragons?
[class.copy]/11 specifies:
A defaulted copy/move constructor for a class
X
is defined as deleted ifX
has:
- [...]
- for the copy constructor, a non-static data member of rvalue reference type
- [...]
An rvalue reference is typically bound to an xvalue or prvalue, i.e. to an expression referring to an object that is "near the end of its lifetime". As lifetime doesn't get extended through function boundaries, it would be error prone to allow such a "copying".