0

Say I have this constructor in C++:

 A::A( std::string const& name,
       std::string const& type,
       std::vector<B> const& b_vec,
       bool unique )
     : _name(name), _type(type), _b_vec(b_vec), _unique(unique)
     { };

I would like to overload this constructor for the case where the arguments are rvalues (I want to use move semantics there).

 A::A( std::string && name,
       std::string && type,
       std::vector<B> && b_vec,
       bool unique )
     : _name(name), _type(type), _b_vec(b_vec), _unique(unique)
     { };

The above one works fine when all of the arguments are rvalues, but suppose if only some of them are is in the next example:

 // create some lvalues somehow
 std::string name   = "stack overflow";
 std::vector<B> vec = { ... }; // implementation of B's constructot is not important

 // call a mixed constructor
 A new_A_instance(name, "cool-website", vec, true);

it is to my understanding that since 'const&' cannot bind to '&&' but '&&' can bind to 'const&' the first (non-move) constructor would be used.

This seems sub-optimal, since two of the four arguments could be moved (because they are rvalue) instead of being copied (as is the case in the first constructor).

So I could overload the operator for this specific case, but one could easily image a case where other arguments are rvalue and others are agin lvalue. Should I overload the constructor for each of these cases? This would combinatorily lead to very much overloads as the number of arguments increases...

I kind-of feel there is a better solution (perhaps using templates, but my template knowledge is shamefully low).

Note: this problem isn't tied to overloading pass-by-ref functions to move functions per-se, but I found this a good example (especially since the overloads don't feel very different). Also note that I just used constructors as an example, but the overloaded function can be anything.

2
  • 1
    An rvalue bool, as well as a const bool, are utterly pointless. Commented Sep 23, 2012 at 20:04
  • @Kerrek: I agree. Don't know why I put them there. Commented Sep 23, 2012 at 20:06

1 Answer 1

2

Pass by value, this is what move semantics are for:

 A::A(std::string name, std::string type, std::vector<B> b_vec, bool unique )
   : _name(std::move(name)), _type(std::move(type)), _b_vec(std::move(b_vec)),
     _unique(unique)
 { };

This has the expected behaviour in every case. Passing a temporary by value allows the compiler to perform copy elision, which it pretty much always does.

Note that in your second code, copies are made, since you don't use std::move. Please realize that when you write

void foo(bar&& x)
{
    ...
}

then in the body of foo, x is a lvalue. Objects with names are always lvalues. Inside this body, you must use std::move(x) if you intend to pass x as a rvalue.

Sign up to request clarification or add additional context in comments.

21 Comments

Actually, the edit isn't quite correct. x is indeed always a bar&&. But it's not an rvalue! Note that bar&& means "rvalue reference", and not "rvalue".
so this code would outperform pass-by-ref code whilst still retaining constness of the passed objects?
@romeovs: yes. Pass by value whenever you can.
@AlexandreC.: I know what you intended, but it's simply not correct. It's no better than claiming that an int-reference is the same as an int. It's not... :-( In other words, an rvalue reference can bind to an rvalue, but it is not itself an rvalue. It's just a reference.
@romeovs: It's something which always existed in fact. See cpp-next.com/archive/2009/08/want-speed-pass-by-value for instance.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.