问题
Can I have class/struct with rvalue field in c++11? Like this one:
template<typename T>
struct RvalueTest{
RvalueTest(T&& value) : value( std::forward<T>(value) ){}
T&& value;
};
Because in the following test:
class Widget {
public:
Widget(){std::cout << "Widget ctor " << std::endl; }
Widget(int h) : h(h){std::cout << "Widget ctor param " << std::endl; }
Widget(const Widget&) { std::cout << "Widget copy ctor " << std::endl; }
Widget(Widget&&) { std::cout << "Widget move ctor " << std::endl; } // added this
template<typename T>
Widget(const T&) { std::cout << "Generalized Widget copy ctor " << std::endl; }
template<typename T>
Widget(T&&) { std::cout << "Universal Widget ctor " << std::endl; }
int h;
};
RvalueTest<Widget> r(Widget(12));
std::cout << r.value.h;
I got some trash value at output (with -O2):
http://coliru.stacked-crooked.com/a/7d7bada1dacf5352
Widget ctor param
4203470
And right value with -O0:
http://coliru.stacked-crooked.com/a/f29a8469ec179046
Widget ctor param
12
WHY???
P.S. What I try to achive is a single ctor call, without any additional move/copy constructors.
UPDATED
It compiles ok with clang http://coliru.stacked-crooked.com/a/6a92105f5f85b943
GCC bug? Or it works as it should ?
回答1:
The problem is not specific to rvalue references. You will see the same odd behaviour, if you replace T&& by const T&.
template<typename T>
struct ReferenceTest{
ReferenceTest(const T& value) : value(value){}
const T& value;
};
The problem is, that you are storing a reference to a temporary object. The temporary object is automatically destroyed at the end of the statement it was created, but you try to access it later via the reference.
RvalueTest<Widget> r(Widget(12)); // pass reference to temporary object to constructor
std::cout << r.value.h; // invalid reference
It is possible to use rvalue references as member variables. However, you have to be careful if doing so. An example from the standard library is std::forward_as_tuple
.
auto t = std::forward_as_tuple(42);
// t has type std::tuple<int&&>
Regarding the question how to avoid the copy or move construction: Don't use rvalue reference member variables. Here are two approaches, you can use instead:
std::unique_ptr: If copying or moving a Widget is too expensive, you can use a smart pointer:
template <typename T>
struct Test
{
std::unique_ptr<T> _value;
explicit Test(std::unique_ptr<T> value) : _value{std::move(value)} { }
};
Test<Widget> t{std::make_unique<Widget>(12)};
std::cout << t._value->h;
The second approach creates the Widget in-place. That's what the different emplace functions of the standard containers do, e.g. std::vector::emplace_back
.
template <typename T>
struct Test
{
T _value;
template <typename ...Args>
explicit Test(Args&&... args) : _value{std::forward<Args>(args)...} { }
};
Test<Widget> t{12};
std::cout << t._value.h;
来源:https://stackoverflow.com/questions/23710910/c11-rvalue-object-field