【问题标题】:Deduction guides, initializer_list, and the type deduction process推演指南、initializer_list、类型推演过程
【发布时间】:2019-04-15 13:48:09
【问题描述】:

考虑following code

#include <initializer_list>
#include <utility>

template<class T>
struct test
{
    test(const std::pair<T, T> &)
    {}
};

template<class T>
test(std::initializer_list<T>) -> test<T>;

int main()
{
    test t{{1, 2}};
}

我想了解为什么 initializer_list 的这个技巧可以编译。乍一看,{1, 2} 被视为initializer_list,但随后重新解释pair 的列表初始化。

这里到底发生了什么,一步一步?

【问题讨论】:

    标签: c++ language-lawyer c++17 initializer-list list-initialization


    【解决方案1】:

    它可以编译,因为这就是类模板推导指南的工作方式。

    演绎指南是该类型的假设构造函数。它们实际上并不存在。它们的唯一目的是确定如何推导出类模板参数。

    一旦进行了推论,实际的 C++ 代码就会使用 test 的特定实例来接管。因此,编译器的行为就像您说的是test&lt;int&gt; t{{1, 2}};,而不是test t{{1, 2}};

    test&lt;int&gt; 有一个构造函数,它接受 pair&lt;int, int&gt;,它可以匹配括号初始化列表中的值,所以这就是被调用的内容。

    这样做的部分原因是允许聚合参与类模板参数推导。聚合没有用户提供的构造函数,因此如果推导指南仅限于真正的构造函数,那么聚合就无法工作。

    所以我们得到了std::array的这个类模板推演指南:

    template <class T, class... U>
    array(T, U...) -> array<T, 1 + sizeof...(U)>;
    

    这允许std::array arr = {2, 4, 6, 7}; 工作。它从指南中推导出模板参数和长度,但由于指南不是构造函数,array 将保持聚合。

    【讨论】:

    • 我了解该指南是一个“假设的”构造函数。让我感到困惑的是,{1, 2} 构造在此过程的不同阶段受到不同的处理......
    【解决方案2】:

    根据您的扣除指南,我们最终得出的结果相当于:

    test<int> t{{1, 2}};
    

    由于列表初始化,这有效,dcl.init.listp3.7 部分说:

    否则,如果 T 是类类型,则考虑构造函数。这 枚举适用的构造函数并选择最好的构造函数 通过重载决议([over.match],[over.match.list])。如果一个 缩小转换(见下文)需要转换任何 论据,程序格式错误。 [ 示例:

    struct S {
      S(std::initializer_list<double>); // #1
      S(std::initializer_list<int>);    // #2
      S();                              // #3
      // ...
    };
    S s1 = { 1.0, 2.0, 3.0 };           // invoke #1
    S s2 = { 1, 2, 3 };                 // invoke #2
    S s3 = { };                         // invoke #3
    

    — 结束示例 ] [ 示例:

    struct Map {
      Map(std::initializer_list<std::pair<std::string,int>>);
    };
    Map ship = {{"Sophie",14}, {"Surprise",28}};
    

    — 结束示例 ] [ 示例:

    struct S {
      // no initializer-list constructors
      S(int, double, double);           // #1
      S();                              // #2
      // ...
    };
    S s1 = { 1, 2, 3.0 };               // OK: invoke #1
    S s2 { 1.0, 2, 3 };                 // error: narrowing
    S s3 { };                           // OK: invoke #2
    

    — 结束示例 ]

    否则我们有一个non-deduced context

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-16
      • 2013-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多