【问题标题】:Why is copy deduction candidate necessary as a separate deduction guide?为什么需要复制扣除候选作为单独的扣除指南?
【发布时间】:2018-05-11 12:57:10
【问题描述】:
template <typename T> struct A {
    A(T);
    A(const A&);
};

int main()
{
    A x(42); // #1
    A y = x; // #2
}

据我了解,#1 的T 将使用隐式演绎指南进行演绎 从第一个 ctor 生成。然后 x 将使用该 ctor 进行初始化。

不过,对于#2,T 将使用复制推导候选来推导(据我所知,这是推导指南的一个特定情况)(然后 y 将使用第二个 ctor 初始化)。

为什么不能使用从 copy-ctor 生成的(隐式)推导指南来推导 #2 的 T

我想我只是不明白复制扣除候选人的一般用途。

【问题讨论】:

  • 不确定您期望什么或不理解什么。 xyA&lt;int&gt;
  • @Jarod42 该问题询问确定yA&lt;int&gt; 的过程,提供了两种不同的确定方法,并询问为什么一种方法还不够。
  • 如果 #2 使用 A::A(T),我们最终会得到 y beeing A>。因此必须首选 A(const A&)。
  • 对于#2 through,T 将使用复制推演候选项推演你是怎么得到的?
  • “为什么不能使用从 copy-ctor 生成的(隐式)推导指南推导出 #2 的 T?” 您应该提供一个示例来说明行为。我试过this,但它有效。

标签: c++ templates c++17 template-argument-deduction


【解决方案1】:

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

本文旨在解决

  • 周一在科纳的 EWG 包装与复制的方向

关于那次会议的一些笔记可在 https://botondballo.wordpress.com/2017/03/27/trip-report-c-standards-meeting-in-kona-february-2017/:

复制与包装行为。假设atuple&lt;int, int&gt;类型的变量,我们写tuple b{a};b 的类型应该是 tuple&lt;int, int&gt;(“复制”行为)还是 tuple&lt;tuple&lt;int, int&gt;&gt;(“包装”行为)?任何类似包装器的类型(例如pairtupleoptional)都会出现这个问题,它既有复制构造函数,又有接受被包装类型对象的构造函数。 EWG 认为复制是最好的默认设置。有人谈到让行为依赖于初始化的语法(例如,{ } 语法应该始终换行),但 EWG 认为在不同初始化语法的行为之间引入新的不一致弊大于利。

@kiloalphaindia explained this in a comment:

如果#2 使用A::A(T),我们最终将使用y 蜜蜂A&lt;A&lt;int&gt;&gt;。 [...]

这是对的。 A&lt;A&lt;int&gt;&gt;::A(A&lt;int&gt;) 构造函数的参数类型完全匹配。另一方面,在这种情况下,A&lt;int&gt;::A(const A&lt;int&gt; &amp;) 应该是首选,这也是对的。

但请考虑这种替代方案,其中等效函数表明如果不是复制扣除候选者,A&lt;A&lt;int&gt;&gt; 将是首选:

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>>
}

【讨论】:

  • 两者都是完全匹配的。
  • @T.C.你是对的,我已经编辑并包含了一个测试作为健全性检查。这个例子对你来说是不是很好,还是你发现了另一个错误?
  • 我觉得不错。
【解决方案2】:

基本问题是,在一般情况下,您不知道某些东西是否是复制/移动构造函数,直到您知道模板参数并实例化特化,但对于 CTAD,您不知道模板参数(duh)并且必须单独通过声明:

template<bool B, class T>
struct A {
   A(std::conditional_t<B, A, int>&); // copy ctor? maybe
   A(std::conditional_t<!B, A&&, char>); // move ctor?

   T t;
   // A(const A&); // implicitly declared? or not? 
                   // is this even the right signature? (depends on T)
   // A(A&&); // implicitly declared? or not?
};
A a = A<true, int>(); // deduce?

复制推导候选解决了这个问题,并且还通过使用按值参数避免了关于值类别和 cv 限定的重载解析细节。

【讨论】:

    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 2020-02-17
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-30
    相关资源
    最近更新 更多