0

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 ?

tower120
  • 5,007
  • 6
  • 40
  • 88

1 Answers1

0

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;
nosid
  • 48,932
  • 13
  • 112
  • 139
  • Another words, how I have to pass that "template" argument Widget(12) so it not call additional constructors. Without overhead. – tower120 May 17 '14 at 12:04
  • And compiler not let you pass template value by "const T& value" (from your example). It just show error there. – tower120 May 17 '14 at 12:08
  • forward_as_tuple tuple loose my data in the same way as in my example http://coliru.stacked-crooked.com/a/302bb62ba3acd5db – tower120 May 17 '14 at 12:49
  • @tower120: I tried to explain, why your code is broken, and that there are valid use cases for rvalue reference member variables. The example with `std::forward_as_tuple` was not meant as a solution to your problem, just as an example for a valid use case. – nosid May 17 '14 at 13:11
  • Than how it should be? I mean, how avoid move/copy constructor. Widget(12) is temporary, after all. – tower120 May 17 '14 at 13:32
  • Here http://stackoverflow.com/questions/9493923/c11-why-is-assigning-rvalues-allowed?rq=1 @Nicol Bolas say that we CAN store at data at rvalue's. – tower120 May 17 '14 at 13:36
  • @tower120: You misunderstand. @Nicol Bolas is saying that if you create a new reference to an existing object, then that reference can be legally accessed. In his example, `y` and `t` refer to the same object. – Mankarse May 17 '14 at 15:04