C++20 中的用户定义推导指南

nic*_*pro 7 c++ language-lawyer type-deduction c++20

我正在std::variant使用“重载”模式std::visit,如下所示:

#include <iostream>
#include <variant>

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

int main(void) {
  std::variant<int, float> var;
  auto fs = overloaded {
    [](int var) {std::cout << "var is int" << std::endl;},
    [](float var) {std::cout << "var is float" << std::endl;}
  };
  var = 0;
  std::visit(fs, var);
  var = 0.0f;
  std::visit(fs, var);
}
Run Code Online (Sandbox Code Playgroud)

在 cppreference 上,有一个示例

// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Run Code Online (Sandbox Code Playgroud)

为什么 C++20 中不再需要这个推导指南?删除类型推导指南会导致编译在所有当前编译器上失败,但目前没有人完全支持 C++20,因此这没有任何意义。谁能指出规范中讨论此问题的部分吗?

Bar*_*rry 5

此更改是由P1021R4添加的,特别是P1816R0,它添加了对聚合的类模板参数推导的支持。

\n

具体写法可以参见[over.match.class.deduct]/1,它基本上添加了聚合的隐式推导指南:

\n
\n

此外,ifC被定义并且其定义满足聚合类 ([dcl.init.aggr]) 的条件,假设任何依赖基类没有虚函数和虚基类,并且初始化器是非空的braced-init-list带括号的表达式列表,并且没有推导指南,该集合包含一个附加函数模板,称为聚合推导候选,定义C如下。\n令x1,\xe2\x80\xa6, xn 是花括号初始化列表表达式列表初始化列表或指定初始化列表的元素。\n对于每个 xi,令 ei 为相应的聚合元素C或其中之一(可能是递归)将由 xi ([dcl.init.aggr]) 初始化的子聚合,如果

\n
    \n
  • 对于具有依赖非数组类型或具有值依赖边界的数组类型的任何聚合元素,不考虑大括号省略,并且
  • \n
  • 假定作为包扩展的每个非尾随聚合元素不对应于初始值设定项列表中的任何元素,并且
  • \n
  • 假设作为包扩展的尾随聚合元素对应于初始值设定项列表的所有剩余元素(如果有)。
  • \n
\n

如果任何 xi 都不存在这样的聚合元素 ei,则聚合推导候选不会添加到集合中。\n聚合推导候选按上述方式从假设的构造函数 C(T1,\xe2\x80\xa6,Tn) 导出,在哪里

\n
    \n
  • 如果 ei 是数组类型并且 xi 是花括号初始化列表或字符串文字,则 Ti 是对 ei 声明类型的右值引用,并且
  • \n
  • 否则,Ti 是 ei 的声明类型,
  • \n
\n

除了 Pj... 形式的附加参数包被插入到参数列表中其原始聚合元素位置中,该位置对应于由于它是参数包而被跳过的类型 Pj 的每个非尾随聚合元素,并且尾随序列与作为包扩展(如果有)的尾随聚合元素相对应的参数被替换为 Tn... 形式的单个参数。

\n
\n

基本上,您之前必须编写的推导指南:

\n
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;\n
Run Code Online (Sandbox Code Playgroud)\n

现在就脱离了语言。

\n

  • 两个链接的提案都没有从包扩展中扣除的措辞。这似乎是在 [p2082r1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2082r1.pdf) 第 4 节中提出的 (3认同)