可变参数模板构造函数和移动构造函数

时间:2016-05-07 21:53:25

标签: c++ c++11 constructor variadic-templates template-meta-programming

我有一个带有可变参数模板构造函数的模板类:

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

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

}

如何定义移动构造函数

MyClass(MyClass&& source)

对于非模板化构造函数,我通常做的是:构建MyClass类型的对象tmp,其std::swap每个成员*this然后我交换{{1}的每个成员与source一起使用。现在*this*包含了所有垃圾tmp,我只是让我的构造函数的范围负责删除*this* ...

但是在这里我被困住了,并且不知道如何构建我的tmp对象。

2 个答案:

答案 0 :(得分:1)

问题是参数Ts&&...可以匹配MyClass<T,nb>&(即非常量)。这使得它在auto b = a;和类似的情况下更好地匹配(因为a不是const)。

因此,您必须使用某些SFNAE魔法禁用该情况,或者提供能够做正确事情的特定重载:

#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();
}

预期产出:

hello, world
goodbye
goodbye
goodbye

答案 1 :(得分:0)

考虑使用 C++17 in_place_t 标记类型来消除采用可变参数的构造函数的歧义。那么就不需要像 Richard 的回答那样显式定义复制/移动构造函数。

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);
}

如果您没有 C++17,您可以轻松地将自己的 in_place_t 标记类型定义为空结构(但不在 std 命名空间内)。