为什么复制扣除候选人需要作为单独的扣除指南?

led*_*ter 13 c++ templates template-argument-deduction argument-deduction c++17

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

int main()
{
    A x(42); // #1
    A y = x; // #2
}
Run Code Online (Sandbox Code Playgroud)

据我了解,T#1将使用第一个ctor生成的隐式演绎指南推导出.然后x将使用该ctor初始化.

但是对于#2,T将使用复制演绎候选人推断(根据我的理解,这是演绎指南的特定情况)(然后y将使用第二个ctor初始化).

为什么不能T使用复制版本生成的(隐含)演绎指南来推导#2?

我想我只是不明白复制演绎候选人的一般目的.

小智 12

添加复制扣除措辞的初稿是P0620R0,其中提到了这一点

本文旨在解决

  • 星期一在科纳举行的EWG包装与复制的方向

有关该会议的一些说明,请访问 https://botondballo.wordpress.com/2017/03/27/trip-report-c-standards-meeting-in-kona-february-2017/:

复制与包装行为.假设a是一个类型的变量tuple<int, int>,我们写tuple b{a};.应该btuple<int, int>("复制"行为)的类型,还是tuple<tuple<int, int>>("包装"行为)?这个问题出现了对任何包装样型(如pair,tupleoptional),其具有两个拷贝构造函数和,是以类型的对象被包裹的构造函数.EWG认为复制是最好的默认.有一些关于使行为依赖于初始化语法的说法(例如{ }语法应该总是换行),但是EWG认为在不同初始化语法的行为之间引入新的不一致会带来弊大于利.

@kiloalphaindia在评论中解释了这一点:

如果#2将使用A::A(T)我们最终会与ybeeing A<A<int>>.[...]

这是正确的.该A<A<int>>::A(A<int>)构造函数的参数类型完全匹配.另一方面,你也是对的,A<int>::A(const A<int> &)在这种情况下,这是首选.

但是考虑一下这个替代方案,其中函数等价显示A<A<int>>如果不是复制演绎候选者则优先考虑:

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

template <typename T> auto f(T &&) -> A<T>;
template <typename T> auto f(const A<T> &) -> A<T>;

int main() {
  A x1(42);                   // A<int>
  A y1 = std::move(x1);       // A<int>

  auto x2 = f(42);            // A<int>
  auto y2 = f(std::move(x2)); // A<A<int>>
}
Run Code Online (Sandbox Code Playgroud)