为什么C ++ 11无法在模板类的构造函数中处理两个模板类型名T == U?

时间:2015-11-06 18:00:01

标签: class templates c++11 copy-constructor move-semantics

  

我找不到简短的标题。 :(

假设我有一个简单的C ++ 11模板类定义如下:

#include <utility>

template <typename T>
class A
{
public:
    T v;
    A(){};
    template <typename U>
    A(const A<U>& a); // copy ctor
    A(A<T>&& a); // move ctor
};

template <typename T>
template <typename U>
A<T>::A(const A<U>& a) // copy ctor
{
    v = a.v;
}

template <typename T> // move ctor
A<T>::A(A<T>&& a)
{
    v = std::move(a.v); // although moving PODs does not make sense in my example
}

现在,我的 C ++ 11代码使用以上C ++ 11类,如下所示:

int main()
{
    A<char> a;
    A<float> b(a); // okay
    A<char> c(a); // gcc output is as below:
                 // error: use of deleted function 'constexpr A<char>::A(const A<char>&)'
                 // note: 'constexpr A<char>::A(const A<char>&)' is implicitly declared
                 // as deleted because 'A<char>' declares a move constructor or move
                 // assignment operator
    return 0;
}

它提供错误use of deleted function 'constexpr A<char>::A(const A<char>&)'

然而,当在类定义中没有使用任何移动语义时,编译并正常运行,如下所示:

#include <utility>

template <typename T>
class A
{
public:
    T v;
    A(){};
    template <typename U>
    A(const A<U>& a);
  //  A(A<T>&& a); // removed move ctor
};

template <typename T>
template <typename U>
A<T>::A(const A<U>& a)
{
    v = a.v;
}

我的问题是:

  1. 为什么gcc编译器在两种情况下以不同方式处理 copy ctor 中的template <typename U>

  2. 为什么存在移动ctor 无法处理类型名称 T == U

  3. 为什么需要明确写入另一个模板 使用时功能template <typename T> A<T>::A(const A<U>& a) 移动ctor

2 个答案:

答案 0 :(得分:4)

您还没有写过复制构造函数。来自C ++ 11标准中的{{1}}:

  

X类的非模板构造函数是一个复制构造函数,如果它的第一个参数是X&amp;类型,const X&amp;,volatile X&amp;或const volatile X&amp;,并且没有其他参数,或者所有其他参数都有默认参数(8.3.6)。

(我看不到将模板构造函数称为复制构造函数的规定)

因此

  

如果类定义没有显式声明复制构造函数,则会隐式声明一个

两个示例之间的区别在于您是否有移动构造函数:

  

如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认(8.4)

答案 1 :(得分:1)

定义移动构造函数禁用了复制构造函数。你想要的是转换构造函数,顾名思义,它将其参数转换为类的类型。如果您的复制/移动构造函数没有做任何特殊操作,请忽略或default它们。为了解释您最后的混淆,您可以在假拷贝构造函数中省略模板参数的原因是因为注入的类名。这意味着无论您在哪里看到A,它都会默默地替换A<T>。为了清楚起见,我把它包括在内。

template <typename T>
class A
{
public:
    T v;
    A() = default;

    template <typename U>
    A<T>(const A<U>& a);

    A(const A<T>& a) = default;
    A(A<T>&& a) = default;
};