1

使用此代码:

template <class T> class Test {
    T _temp;

public:
    Test() {
        std::cout << "Test()" << std::endl;
    };

    template <class T2> Test(Test<T2> const &test) {
        std::cout << "template <class T2> Test(Test<T2> const &test)" << std::endl;
    };

    template <class T2> Test(Test<T2> &&test) {
        std::cout << "template <class T2> Test(Test<T2> &&test)" << std::endl;
    };

};

使用此测试代码:

Test<int> testInt;
Test<float> testFloat(testInt);
Test<float> testFloat2(std::move(testInt));

std::cout << "----------" << std::endl;

Test<int> testInt2;
Test<int> testInt3(testInt2);
Test<int> testInt4(std::move(testInt2));

产生这个输出:

Test()
template <class T2> Test(Test<T2> const &test)
template <class T2> Test(Test<T2> &&test)
----------
Test()

当使用相同类型时,使用默认的复制和移动构造函数代替转换构造函数。

但是如果我在类中添加默认的复制构造函数:

Test(Test const &test) = default;

它产生这个输出:

Test()
template <class T2> Test(Test<T2> const &test)
template <class T2> Test(Test<T2> &&test)
----------
Test()
template <class T2> Test(Test<T2> &&test)

即使使用相同的类型也会调用移动转换构造函数,为什么?

有没有办法统一复制和转换构造函数以避免重复代码?

4

1 回答 1

2

添加隐式生成(= 编译器生成)移动构造函数的规则非常保守:仅在安全的情况下添加一个。如果用户已经定义了复制构造函数,编译器不能假设添加一些简单的编译器生成的移动构造函数仍然是安全的,所以不会添加移动构造函数。实际上,声明它足以防止移动构造函数的生成,IIRC 允许仅复制类型。

构造函数模板从不被视为复制构造函数或移动构造函数,因此它们不会阻止隐式生成它们。

当然,如果从未声明移动构造函数,则可以通过重载决议选择另一个构造函数。考虑这个例子:

#include <iostream>
#include <utility>

struct loud
{
    loud() { std::cout << "default ctor\n"; }
    loud(loud const&) { std::cout << "copy ctor\n"; }
    loud(loud&&) { std::cout << "move ctor\n"; }
};

struct foo
{
    loud l;
};

struct bar
{
    loud l;
    bar() = default;
    bar(bar const&) = default;
};

int main()
{
    foo f0;
    foo f1(f0);
    foo f2(std::move(f0));

    std::cout << "--------------\n";

    bar b0;
    bar b1(b0);
    bar b2(std::move(b0));
}

输出:

默认 ctor
复制者
移动 ctor
--------------
默认 ctor
复制者
复制者

在这里,foo将获得隐式声明的默认、复制和移动构造函数,而bar不会获得隐式声明的移动构造函数,因为它具有用户声明的复制构造函数


在第二种情况下,没有隐式声明的移动构造函数,因此重载解析Test<int> testInt4(std::move(testInt2))更喜欢构造函数模板而不是复制构造函数,因为前者采用的引用较少 cv 限定。


为了减少代码重复,您可以委托构造:

template <class T>
class Test {
    T _temp;

    struct tag {};

public:
    Test() {
        std::cout << "Test()" << std::endl;
    };

    Test(Test const& test)
        : Test(test, tag{})
    {}

    Test(Test&& test)
        : Test(std::move(test), tag{})
    {}

    template <class T2> Test(Test<T2> const &test, tag = {}) {
        std::cout << "template <class T2> Test(Test<T2> const &test)" << std::endl;
    };

    template <class T2> Test(Test<T2> &&test, tag = {}) {
        std::cout << "template <class T2> Test(Test<T2> &&test)" << std::endl;
    };

};

顺便说一句,您还可以通过使用完美转发构造函数模板而不是两个构造函数模板来减少一些样板:

使用这个特性:

template<class T>
struct is_Test : std::false_type {};
template<class T>
struct is_Test<Test<T>> : std::true_type {};

您可以定义:

    template <class T2,
              class = typename std::enable_if<is_Test<T2>::value>::type>
    Test(T2&& test, tag = {}) {
        std::cout << "template <class T2> Test(T2&& test)" << std::endl;
    };
于 2014-03-30T21:59:09.297 回答