【问题标题】:Why is there a special type deduction rule for auto and braced initializers in C++11/C++14?为什么 C++11/C++14 中的自动初始化器和大括号初始化器有特殊的类型推导规则?
【发布时间】:2015-06-16 09:18:36
【问题描述】:

在他的 CppCon 2014 talke "Type Deduction and Why You Care" 中,Scott Meyers 提出了一个问题,为什么在 C++11/C++14 标准中有关于 auto 和大括号初始值设定项的特殊规则(他的问题始于 at 36m05s) .

auto 与花括号初始化列表结合的语义在 §7.1.6.4/6 中定义。


我想了想,也想不出用例。到目前为止,我看到的最接近的事情是 Bjarne Stroustrup 使用它的一个例子。

在他的Cpp 2014 talk "Make Simple Tasks Simple!" 中,他曾经使用auto 来捕获初始化程序(但仅作为一种解决方法)。

这是代码(幻灯片 30 的一部分,at 37m10s):

    // auto ss1 = collect({ 1, 2, 3, 4, 5, 6 }, odd); // error: Bummer!
    auto lst = { 1, 2, 3, 4, 5, 6 };
    auto ss2 = collect(lst, odd);    // {1,3,5}

但请注意,这只是一种解决方法。他提到这应该没有必要。相反,他更愿意直接将参数传递给函数。因此,它不能真正成为 auto 和初始化列表的良好动机。


我对 C++ 的理解还不够深入,无法判断在 Bjarne 的示例中允许初始化列表的缺点,正如他所建议的那样。无论如何,在这种情况下,它会避免使用auto。

那么,auto 和初始化列表是否只是解决本可以更好解决问题的解决方法?或者有没有很好的例子,第 7.1.6.4/6 节中的额外自动扣除规则有用?

【问题讨论】:

  • 我很确定有一个 C++1z 提议让auto x = {expr}; 使用expr 的类型(减去允许使用非括号逗号运算符)。不过,我不确定要为完整列表使用什么其他类型。
  • @christ 我想你的意思是 N3922。斯科特迈耶斯也提到了这一点。 Visual Studio 已经实现了它,即使它不是 C++14 的一部分。这是链接:open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3922.html
  • 啊,就是这个。尽管由于这种用法,我有点不得不不同意一般的说法。不过,我真的更希望它是格式错误的,并且初始化列表能够在没有额外行的情况下传递给函数。

标签: c++ c++11 auto c++14 initializer-list


【解决方案1】:

原因在于N2640,它希望禁止从大括号初始值设定项列表中扣除普通类型参数:

template<class T>
void inc(T, int); // (1)

template<class T>
void inc(std::initializer_list<T>, long); // (2)

inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded
                   // for (1), (1) would have been called — a
                   // surprise.)

但是为auto开辟了一个特殊的例外:

另一方面,能够推断出initializer_list&lt;X&gt; T 很有吸引力:

auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);

从一开始就被认为是可取的行为 EWG 关于初始化列表的讨论。而不是想出一个 与 {}-list 匹配的参数类型 T 的巧妙推导规则 (我们在本文早期的草图和草稿中采用的一个选项), 我们现在更喜欢用“auto”变量的特殊情况来处理这个问题 当初始值设定项是 {}-list 时的推导。即,对于特定的 使用“auto”类型说明符和一个声明的变量的情况 {}-list 初始值设定项,“auto”被推断为函数 f(initializer_list&lt;T&gt;) 而不是函数f(T)。

【讨论】:

    【解决方案2】:

    Scott Meyers 在一篇博文中谈到了这个话题:Why auto deduces std::initializer_list for a braced initializer

    就像 T.C. 的回答一样,它也指的是N2640。添加了特殊的扣除规则以允许这样的代码工作:

     auto x = { 1, 1, 2, 3, 5 };
     f(x);
     g(x);
    

    在博文中,Scott 引用了 James Hopkin 的以下解释:

    简短的故事是 N2640 提出了一种特殊情况,即 auto 应该将大括号初始化器推导出为 initializer_lists,但没有意识到这样做会破坏统一初始化(例如,它使 int x{7}; 和 auto x{7}; 非常不同)。 N3922 通过(当然!)引入另一种特殊情况来解决这个问题:单参数大括号初始化器有自己的规则。

    稍微详细一点:N2640 试图保持模板参数推导简单,但试图通过将花括号初始化器分配给 auto 来将其传递给两个或多个函数。这变成了N2672 中的措辞。请注意,Stroustrup 之前在N2532 中的设计允许对无约束模板参数和 auto 都进行 initializer_lists 的推导,这样更加一致但也破坏了统一初始化。

    这些都不能解释为什么N3922 不只是删除 auto 的特殊情况。这不会导致对代码含义的无声更改,并且会简化语言。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-06
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多