How to use variable and move variable at the same time?

Viewed 194

Let's assume we have the following code:

struct some_class : parent
{

    some_class(::other_class oth) :
       parent(some_function(oth.some_property), std::move(oth))
    {}

};

Of course construction results in undefinied behaviour (crash in my case), since c++ does not specify execution order. But then, how can I retreive the property before the movement? I cannot change the parent.

3 Answers

Create a helper function to construct the parent where you can add sequencing:

parent make_parent(::other_class &&oth) {
    auto sf = some_function(oth.some_property);
    return parent(sf, std::move(oth));
}

some_class(::other_class oth) :
    parent(make_parent(std::move(oth))
{}

As you note the issue is due to unspecified execution order. You can get rid of the undefined behavior by making parent take an object by rvalue reference instead of by lvalue. That way it gets a reference to an existing object and that object's data in memory won't actually have been moved. I.e.:

struct parent {
   parent (int thing, SomeProperty && some_property) { /* Do stuff. */ }
};

In this case it doesn't matter when std::move is executed on oth. It's data won't actually be moved into another object, since parent expects an rvalue reference. So even if std::move is called first, your some_function (which I'm assuming is taking oth by const lvalue reference) will have a proper object to work with.

The main drawback here is that parent now always requires in rvalue. So you can't pass it an object without moving it. Which means that if you have an object you don't want to get rid of, you'll first have to explicitly make a copy of it. I.e:

other_class wantToKeepThis;
auto foo = parent(wantToKeepThis); // Doesn't compile.
auto foo = parent(std::move(wantToKeepThis)); // Object gone, don't want this.
auto foo = parent(other_class(wantToKeepThis)); // OK, copied, parent gets rvalue.

You could try a delegating constructor:

struct some_class : parent
{

    some_class(::other_class oth) :
       some_class(some_function(oth.some_property), std::move(oth))
    {}

private:
    some_class(const ::Foo& foo, ::other_class&& oth) :
       parent(foo, std::move(oth))
    {}
};
Related