c++11 optimal parameter passing

c++, c++11, parameter-passing, rvalue-reference

Solution

The "by-value" approach is an option. It is not as optimal as what you have, but only requires one overload:

class B
{
    A a;
public:   
    B (A _a) : a(move(_a)) {}
};

The cost is 1 extra move construction for both lvalues and xvalues, but this is still optimal for prvalues (1 move). An "xvalue" is an lvalue that has been cast to rvalue using std::move.

You could also try a "perfect forwarding" solution:

class B
{
    A a;
public:   
    template <class T,
              class = typename std::enable_if
              <
                 std::is_constructible<A, T>::value
              >::type>
    B (T&& _a) : a(std::forward<T>(_a)) {}
};

This will get you back to the optimal number of copy/move constructions. But you should constrain the template constructor such that it is not overly generic. You might prefer to use is_convertible instead of is_constructible as I've done above. This is also a single constructor solution, but as you add parameters, your constraint gets increasingly complicated.

Note: The reason the constraint is necessary above is because without, clients of `B` will get the wrong answer when they query `std::is_constructible<B, their_type>::value`. It will mistakenly answer true without a proper constraint on `B`.

I would say that none of these solutions is always better than the others. There are engineering tradeoffs to be made here.

Problem

Consider these classes: ``` #include <iostream> #include <string> class A { std::string test; public: A (std::string t) : test(std::move(t)) {} A (const A & other) { *this = other; } A (A && other) { *this = std::move(other); } A & operator = (const A & other) { std::cerr<<"copying A"<<std::endl; test = other.test; return *this; } A & operator = (A && other) { std::cerr<<"move A"<<std::endl; test = other.test; return *this; } }; class B { A a; public: B (A && a) : a(std::move(a)) {} B (A const & a) : a(a) {} }; ``` When creating a `B`, I always have an optimal forward path for `A`, one move for rvalues or one copy for lvalues. Is it possible to achieve the same result with one constructor? It's not a big problem in this case, but what about multiple parameters? I would need combinations of every possible occurrence of lvalues and rvalues in the parameter list. This is not limited to constructors, but also applies to function parameters (e.g. setters). Note: This question is strictly about `class B`; `class A` exists only to visualize how the copy/move calls gets executed.

Original source

Related problems