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

User-Defined Deduction Guides in C++20

我正在使用 std::variantstd::visit 使用 "overload" pattern,看起来像这样:

#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);
}

在 cppreference 上,an example 表示:

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

为什么 C++20 不再需要这个推导指南?删除类型推导指南会导致 compile to fail on all current compilers,但目前没有人完全支持 C++20,因此这没有任何意义。谁能指出规范中讨论此问题的部分?

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

具体的措辞可以在[over.match.class.deduct]/1中找到,其中基本上添加了聚合的隐式推导指南:

In addition, if C is defined and its definition satisfies the conditions for an aggregate class ([dcl.init.aggr]) with the assumption that any dependent base class has no virtual functions and no virtual base classes, and the initializer is a non-empty braced-init-list or parenthesized expression-list, and there are no deduction-guides for C, the set contains an additional function template, called the aggregate deduction candidate, defined as follows. Let x1,…,xn be the elements of the initializer-list or designated-initializer-list of the braced-init-list, or of the expression-list. For each xi, let ei be the corresponding aggregate element of C or of one of its (possibly recursive) subaggregates that would be initialized by xi ([dcl.init.aggr]) if

  • brace elision is not considered for any aggregate element that has a dependent non-array type or an array type with a value-dependent bound, and
  • each non-trailing aggregate element that is a pack expansion is assumed to correspond to no elements of the initializer list, and
  • a trailing aggregate element that is a pack expansion is assumed to correspond to all remaining elements of the initializer list (if any).

If there is no such aggregate element ei for any xi, the aggregate deduction candidate is not added to the set. The aggregate deduction candidate is derived as above from a hypothetical constructor C(T1,…,Tn), where

  • if ei is of array type and xi is a braced-init-list or string-literal, Ti is an rvalue reference to the declared type of ei, and
  • otherwise, Ti is the declared type of ei,

except that additional parameter packs of the form Pj... are inserted into the parameter list in their original aggregate element position corresponding to each non-trailing aggregate element of type Pj that was skipped because it was a parameter pack, and the trailing sequence of parameters corresponding to a trailing aggregate element that is a pack expansion (if any) is replaced by a single parameter of the form Tn....

基本上,你之前要写的演绎指南:

template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

现在刚脱离语言。