0

Let me have a custom wrapper container. I want to use it like this:

double d = 3.14;
MyContainer<std::vector<int>> pointer = new std::vector<int>();
MyContainer<std::string> rvalue = std::string("foo");
MyContainer<int> rvalue2 = 5 + 8;
MyContainer<double> lvalue = d;

I don't want to store copies of rvalues (a reference is OK). Rvalue references allow me to do like this:

std::string string1 = "foo";
std::string string2 = "bar";
std::string&& string3 = string1 + string2;
string3 += "test";

Basically I want to extend rvalues' lifetime to my container's lifetime. However when I do this:

template<class T>
class MyContainer {
public:
    MyContainer(T&& obj) : object(obj) {}
    T&& object
    ...
};
...
MyContaier<std::string> container = MyContainer(std::string("foo"));

I get an error (cannot bind 'std::string' lvalue to 'std::string&&'). The example is slightly different, but I just want to understand a general idea. How can I avoid this?

  • do you mean `T&& object;` in `MyContainer` and the container is actually a "container of references" ? If so then your `std::vector` example would fail to compile, and also this has different semantics to your other cases (you used dynamic allocation for that case) – M.M May 13 '16 at 05:17
  • `object(obj)` should be `object(std::move(obj))` , this is probably your compiler error but it doesn't fix the lifetime issue (you now have silent undefined behaviour) – M.M May 13 '16 at 05:17
  • The only way to make lifetime extension work is if `MyContainer` is an aggreagate, and you use aggregate initialization, [see here](http://stackoverflow.com/questions/23892018/extending-temporarys-lifetime-through-rvalue-data-member-works-with-aggregate). I.e. you can't have any user-defined constructors in that case. Otherwise you are just going to have to give it value semantics. – M.M May 13 '16 at 05:24
  • Thanks, that's clear now. – Vladimir Bogachev May 13 '16 at 05:29

1 Answers1

0

Besides that your code has multiple typos and syntax errors, there was nothing technically preventing you from taking an rvalue reference of std::string (although your assignment/constructor call is incorrect). Keeping a T&& as a member variable doesn't work like you think it does. Storing a reference to an expired rvalue and then accessing it would be UB as soon as you reach the next sequence point.

Here's a working example with a constructor for rvalue references and lvalue references. You need an actual instance inside your object if you want to "own" it. You can't technically extend the lifetime of an expiring rvalue; you can only construct something else that reuses it (and hopefully steals some of its expensive guts). Hope this helps.

#include <utility>
#include <string>
#include <iostream>

template<class T>
class MyContainer {
public:

    // makes a copy from an lvalue reference
    MyContainer(const T& obj)
        : object(obj) {
    }

    // moves from an rvalue reference
    MyContainer(T&& obj)
        : object(std::move(obj)) {
    }

    MyContainer& operator=(const T& obj) {
        object = obj;
    }

    MyContainer& operator=(T&& obj) {
        object = std::move(obj);
    }

    T object;
};

int main() {

    MyContainer<std::string> container = std::string("foo");
    std::cout << container.object;
}
Barrett Adair
  • 1,306
  • 10
  • 24
  • Downvote? This code is entirely correct and answers the question. – Barrett Adair May 13 '16 at 05:36
  • I should have given more information about the container. I want it to be sort of a smart pointer. So there could technically be multiple copies of it bound to the same object. That's why I don't want to keep a copy of an object. Rvalue references can effectively extend lifetime, but it looks like I have to use aggregate initialization here. – Vladimir Bogachev May 13 '16 at 05:40