tm8cc
tm8cc

Reputation: 1131

variadic template constructor and move constructor

I have an template class with a variadic template constructor:

template<typename T, int nb = 1>
class MyClass
{
 /* ... some stuff here ... */

    template<typename... Ts>
    MyClass(Ts... ts)
    {
         /* ... some code ... */
    }

}

How do I define the move constructor

MyClass(MyClass&& source)

For non templated constructors what I usually do is : build an object tmp of type MyClass for which I std::swap every member with *this and then I swap every member of source with *this*. Now tmpcontains all the garbage *this* had and I just let the scope of my constructor taking care of deleting tmp...

But here I'm stuck and don't know how to construct my MyClass tmp(???) object.

Upvotes: 3

Views: 1181

Answers (2)

Emile Cormier
Emile Cormier

Reputation: 29219

Consider using the C++17 in_place_t tag type to disambiguate your constructor taking variadic arguments. Then there is no need to explicitly define the copy/move constructors like in Richard's answer.

template<typename T, int nb = 1>
class MyClass
{
 /* ... some stuff here ... */

    template<typename... Ts>
    MyClass(std::in_place_t, Ts... ts)
    {
         /* ... some code ... */
    }

}

int main()
{
    MyClass foo(std::in_place, 123, 456);
    MyClass bar(foo);
}

If you don't have C++17, you can easily define your own in_place_t tag type as an empty struct (but not within the std namespace).

Upvotes: 0

Richard Hodges
Richard Hodges

Reputation: 69902

The problem is that the argument Ts&&... can match a MyClass<T,nb>& (i.e. non-const). This makes it a better match in the case of auto b = a; and similar (because a is not const).

So you'll either have to disable that case with some SFNAE magic or provide a specific overload that does the right thing:

#include <memory>
#include <string>
#include <iostream>

template<typename T, int nb = 1>
class MyClass
{
    /* ... some stuff here ... */

public:
    template<typename... Ts>
    MyClass(Ts&&... ts)
    : _pstr(std::make_shared<std::string>(std::forward<Ts>(ts)...))
    {
    }

    // match the specific case, and force a copy
    MyClass(MyClass<T, nb>& r)
    : MyClass(static_cast<const MyClass<T, nb>&>(r))
    {}

    // and now we must match all copy/move operations        
    MyClass(const MyClass<T, nb>&) = default;
    MyClass& operator=(const MyClass<T, nb>&) = default;
    MyClass(MyClass<T, nb>&&) = default;
    MyClass& operator=(MyClass<T, nb>&&) = default;

    void print() const {
        std::cout << *_pstr << std::endl;
    }
private:
    std::shared_ptr<std::string> _pstr;

};

// test
int main()
{
    auto a = MyClass<int>("hello, world");
    auto b = a;
    auto c = MyClass<int>("goodbye");
    auto d = c;
    b = c;
    a.print();
    b.print();
    c.print();
    d.print();
}

expected output:

hello, world
goodbye
goodbye
goodbye

Upvotes: 2

Related Questions