KOLANICH KOLANICH - 2 months ago 12
C++ Question

Automatic implicit generation of ctors utilising move semantics

Let we have a class with n fields. Each field can be moved. So do we have to explicitly define 2^n constructors?

The example with n=2:

struct A{
std::vector<B> a;
std::shared_ptr<B> b;
A(std::vector<B> &a, std::shared_ptr<B> &b):a(a),b(b){};
A(std::vector<B> &a, std::shared_ptr<B> &&b):a(a),b(std::move(b)){};
A(std::vector<B> &&a, std::shared_ptr<B> &b):a(std::move(a)),b(b){};
A(std::vector<B> &&a, std::shared_ptr<B> &&b):a(std::move(a)),b(std::move(b)){};
};
....
A aaa({{},{}}, shared_ptr<B>(new B()));
std::shared_ptr<B> b = ....;
A bbb({{},{}}, b);

Answer

You can work around it by having a perfect-forwarding template constructor:

struct A {
    std::vector<B> a;
    std::shared_ptr<B> b;
    template <typename AA, typename BB> A(AA&& a, BB&& b) :
        a(std::forward<AA>(a)), b(std::forward<BB>(b)) { }
};

If you need stricter parameter types requirement, you can also add enable_if or static_assert.

Here's a little explanation how perfect forwarding works:

void func1(int&&) { }
template <typename A> void func2(A&& t) {
    func3(t);
    func4(std::forward<A>(t);
}
template <typename B> void func3(B&&) { }
template <typename C> void func4(C&&) { }

int foo;
const int bar;

func1(foo); // ERROR
func1(bar); // ERROR
func1(std::move(foo)); // OK

func2(foo); // OK, A = int&, B = int&, C = int&
func2(bar); // OK, A = const int&, B = const int&, C = const int&
func2(std::move(foo)); // OK, A = int&&, B = int&, C = int&& <- note how && collapses to & without std::forward
Comments