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

Why does C++11 fail to treat two template typenames T == U in constructor in a template class?

I could not find a short and better title. :(

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

#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 class 如下:

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>&)'

但是,当我没有在class定义中使用任何移动语义时,它可以正常编译和运行,如下所示:

#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. 为什么 无法处理类型名称 T == U 移动构造函数的存在?

  3. 为什么我还需要显式编写另一个模板 使用时函数 template <typename T> A<T>::A(const A<U>& a) 移动构造函数?

你还没有写拷贝构造函数。来自 C++11 标准中的 [class.copy]

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).

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

因此

If the class definition does not explicitly declare a copy constructor, one is declared implicitly

两个例子的区别在于你有没有移动构造函数:

If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4)

定义移动构造函数禁用了复制构造函数。您想要的是一个 转换构造函数 ,顾名思义,它将其参数转换为 class 的类型。如果您的 copy/move 构造函数没有做任何特殊的事情,则忽略或 default 它们。为了解释你最后的困惑,你可以在你的假复制构造函数中省略模板参数的原因是因为 注入的 class 名称 。这意味着无论您在哪里看到 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;
};