【问题标题】:Why can't I initialize a value using braces with auto and pass it into this function为什么我不能使用带有 auto 的大括号初始化一个值并将其传递给这个函数
【发布时间】:2012-08-26 19:49:06
【问题描述】:

为什么我不能用 auto 初始化一个值并将其传递给一个需要 decltype 作为参数的函数?

让我来设置场景,给你看一个小程序。


这是一个返回值的函数。

int Function(void);

在这种情况下,它恰好是一个整数,但返回类型可能会发生变化。
这就是为什么下一个函数写成如下:

void What_I_Take_Depends_On_Function(decltype(Function()) x);

如果有人决定改变Function的返回类型,那么this函数的减速就不需要改变了。是的,函数的定义可能无法正确处理新类型,或者如果函数的返回类型更改为 void 会有问题,但这与我的问题无关。

#include <iostream>
#include <cstdlib>

int Function(void){return 5;}

void What_I_Take_Depends_On_Function(decltype(Function()) x){return;}

int main(){

    //assignments(Edit: these are initializations)
    int  var1 = Function();
    auto var2 = Function();

    //initializations
    int  var3 {Function()};
    auto var4 {Function()};

    What_I_Take_Depends_On_Function(var1); //works
    What_I_Take_Depends_On_Function(var2); //works
    What_I_Take_Depends_On_Function(var3); //works
    What_I_Take_Depends_On_Function(var4); //COMPILER ERROR

    //cannot convert ‘std::initializer_list<int>’ to ‘int’ for argument ‘1’ to ‘void What_I_Take_Depends_On_Function(int)’

    return EXIT_SUCCESS;
}

那么为什么 var4 是 initializer_list 而不是 int?
不能自动推断 Function 将返回一个 int,
然后把减速改成和var3类似的?

【问题讨论】:

  • 嗯,因为{...} 是一个初始化列表。 auto 尝试匹配你给它的东西,你给它一个初始化列表。也许你的意思是auto var4 (Function())? (括号,而不是大括号。)
  • @RaymondChen 我不明白如何给它一个初始化列表必然会推导出一个初始化列表。
  • 因为这就是标准 7.1.6.4(2) 所说的发生。 "从T 获取P,方法是将出现的auto 替换为新发明的类型模板参数U,或者如果初始值设定项是braced-init-list (8.5.4),则使用@987654332 @"
  • 顺便说一句,您在var1var2 之前的评论是错误的。它们可能看起来像赋值,但它们实际上是初始化 (8.5(1))。
  • @RaymondChen 但你说得好像他应该事先知道。匹配一个初始化列表并不一定意味着它必须是 initializer_list。也可以是vector&lt;int&gt;

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


【解决方案1】:

bracket-init-list {...} 是 C++11 新的统一初始化语法,它可用于初始化任何自动、静态或成员变量如果该变量的类型已知。以下是一些有效的 C++11 初始化示例:

class S
{
private:
  int _m;
public:
  S(int m) : _m { m }  // <== initialization of a member
  {}
};


int main() {

  constexpr unsigned i { 10 }; // <== initialization of a constexpr

  S s { i };  // <== initialization by calling constructor

  /* ... */
}

以上都没有的情况下会生成std::initializer_list。大括号初始化列表{...} 仅仅意味着编译器将尝试为给定的数据类型识别一个合适的构造函数,其参数列表与大括号初始化列表的内容相匹配(第 8.5 节)。 4).

两种特殊情况:

  1. 如果为给定数据类型定义的构造函数之一std::initializer_list&lt;T&gt; 作为参数,并且大括号初始化列表的内容都是可隐式转换的到T。当您使用其中一种内置容器类型时就是这种情况,例如

    std::vector<int> vec { 1, 2, 3 };
    

    std::vector&lt;T&gt; 有一个构造函数std::vector&lt;T&gt;(const std::initializer_list&lt;T&gt;),因此大括号初始化列表将被转换为std::initializer_list&lt;int&gt; 并因此复制到向量构造函数中。构造函数将遍历列表并一一追加元素。

    当列表只包含一个参数时,这有点陷阱:

    std::vector<int> vec { 10 };
    

    在这里,同样的事情发生了,所以你会得到一个包含一个元素 10 的向量。这与使用旧式语法不同:

    std::vector<int> vec(10);
    

    这会调用std::vector&lt;int&gt;(const size_t) 构造函数,即它会创建一个包含 10 个默认初始化元素的向量。

  2. 如果要初始化的变量的类型不是预先确定的,即在声明中使用auto时:

    auto v { 1, 2, 3 };
    

    在这种情况下(第 7.1.6.4/6 节),编译器无法识别合适的构造函数,因为 任何 采用三个整数(或可转换为整数)参数的数据类型都是可能的候选者.这里的规则是编译器假定std::initializer_list&lt;int&gt; 作为v 的数据类型。这也是你的情况。

换句话说,使用大括号初始化列表很好(甚至鼓励)初始化,但你不能轻易地将它与auto结合起来。要解决您的问题,您需要明确声明数据类型

int var4 { Function() };

或者,为了保持灵活性,也可以在这里使用decltype

decltype(Function()) var4 { Function() };

或者,您可以使用旧式语法:

auto v (Function());

【讨论】:

    【解决方案2】:

    不,auto 不会“只是推断 Function 将返回一个 int”; auto 说明符的工作方式在 7.1.6.4 中已明确定义,如下所示:

    (7.1.6.4/p6) 一旦根据 8.3 确定了 declarator-id 的类型,声明变量的类型 使用 declarator-id 由使用模板参数规则的初始值设定项类型确定 扣除。让T 成为已为变量标识符d 确定的类型。从T获取P 用新发明的类型模板参数U 替换出现的auto,或者,如果初始化程序 是braced-init-list (8.5.4),与std::initializer_list&lt;U&gt;。然后为变量 d 推导出的类型是 推导的A 使用从函数调用 (14.8.2.1) 的模板参数推导规则确定, 其中P 是函数模板参数类型,d 的初始值设定项是相应的参数。如果 扣除失败,声明格式不正确。

    【讨论】:

      【解决方案3】:

      我对语法有误解。
      我习惯于这样初始化变量:

      int i{0};
      

      但是,我需要用括号而不是括号来初始化这种类型。
      谢谢雷蒙德

      【讨论】:

      • 你说的不对。当然你可以用int i{0};声明一个整型变量i并用0初始化。
      猜你喜欢
      • 1970-01-01
      • 2016-02-25
      • 2013-06-03
      • 2018-03-06
      • 2022-06-10
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多