模板构造函数无法选择?

goo*_*era 6 c++ templates constructor language-lawyer

template <typename T>
class A {
public:
    template<class C> A(const A<C>&) {}
    A(A&&) {}
};

void f(A<int>& a)
{
    A<int> b(a);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码不能在g ++/clang ++中编译报告复制构造函数因为用户提供的移动构造函数而被删除(尽管vc ++编译好了).是否有任何标准要求阻止在重载解析期间选择模板构造函数(我知道它不是复制构造函数)?或者是否要求初始化程序与initializee具有相同的类型时,它必须选择复制构造函数?

Fil*_*efp 4

笔记; gccclang在尝试编译提供的代码片段时显示正确的行为。


为什么vc++接受片段是错误的?

  1. 函数模板永远不能以生成复制构造函数的方式实例化,可以从 C++ 标准中的以下两个引用中读取:

    [类. 副本]

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

    ...

    6) 如果类的X第一个参数是类型(可选 cv 限定)X并且没有其他参数或所有其他参数都有默认实参,则类的构造函数的声明是格式错误的。成员函数模板永远不会被实例化以产生这样的构造函数签名。

  2. 如果显式声明了移动构造函数,则隐式生成的复制构造函数将不可调用,请参阅以下内容:

    [类. 副本]

    7) 如果类定义没有显式声明复制构造函数,则隐式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为deleted;否则,它被定义为默认(8.4)


struct Obj {
  template<typename T>
  Obj (T const&) { }
  Obj (Obj&&) { }
};

...

Obj a;
Obj b (a);
Run Code Online (Sandbox Code Playgroud)

考虑到前面提到的 C++ 标准的引用,我们可以很容易地看到上面的定义在语义上与Obj下面的定义等效,因为我们显式声明的move-constructor将使我们隐式声明的 copy-constructor = delete;

struct Obj {
  template<typename T>
  Obj (T const&) { }
  Obj (Obj const&) = delete;
  Obj (Obj&&) { }
};
Run Code Online (Sandbox Code Playgroud)

根据重载决策的规则,删除的函数在尝试找到最佳匹配时仍然参与,但如果它们赢得了最佳匹配的战斗,则程序是错误的。这正是片段中发生的情况。

Obj (Obj const&) = delete比 更适合template<typename T> Obj (T const&),但由于复制构造函数不可调用,因此代码片段无法编译。