C++11 rvalue object field

陌路散爱 提交于 2019-12-25 07:10:02

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!