如果不是initializer_list,什么是花括号包围的列表?

32

我在这里提出了一个问题:initializer_list返回值的生命周期延长,其中涉及到以下非功能性代码:

const auto foo = [](const auto& a, const auto& b, const auto& c) { return {a, b, c}; };

我原以为lambda表达式尝试返回一个intializer_list(这是不好的,不要这样做)。但我得到了一个评论

它不是initializer_list,它是初始化列表。这是两个不同的东西。

我只是认为任何时候你用大括号创建一个列表就会创建一个intializer_list。如果这不是发生的事情,那么花括号中的列表是什么?


只是一个错误:返回初始化列表。 - user2249683
1
@DieterLücking,你的错误提示可能比我高。第一次尝试时,我甚至没有收到警告,尽管如链接问题所述,这不是好的代码。 - Jonathan Mee
1
@Arunmu,你是说这些只是与initializer_list相关的好知识吗?因为如果你直接说它与之有关,我不明白。 - Jonathan Mee
如果您能引用http://www.stackoverflow.com的标题标准,我会修复这个问题以及所有未来的标题。我正在使用[Title Case](https://en.wikipedia.org/wiki/Letter_case#Headings_and_publication_titles),我认为这是标准的。 - Jonathan Mee
我可以在谷歌上搜索有关大写字母影响可读性的内容,但这都是主观的,互联网上没有“官方”标准。这只是我的观察。如果您不同意,我不会争论。 - user1306322
显示剩余3条评论
4个回答

30
这是一个“花括号初始化列表”。在 std::initializer_list 存在之前,花括号初始化列表 就已经存在了,并用于初始化集合
int arr[] = {1,2,3,4,5};

上面使用了一个花括号初始化列表来初始化数组,没有创建std :: initializer_list。另一方面,当您执行以下操作时:
std::vector<int> foo = {1,2,3,4,5};

foo不是一个聚合体,因此使用大括号初始化列表创建std::initializer_list,然后将其传递给接受std::initializer_listfoo构造函数。

值得注意的是,大括号初始化列表没有类型,因此针对它和auto开发了特殊规则。自从采用N3922以来,它具有以下行为。

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int

您可以在为什么auto x{3}会被推导为initializer_list?了解更多关于这种行为的历史以及为什么它发生了改变的信息。


1
@JonathanMee 我认为更新现在应该已经解决了这个问题。 - NathanOliver
1
@T.C. 我甚至没有看到“非终端”这个词,你在说什么呢? - Jonathan Mee
@T.C. 在今天之前,我一直认为它是一个“initializer_list” ;) 我猜你是告诉我“nonterminal”是另一个名称? - Jonathan Mee
1
@JonathanMee https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols braced-init-list 是C++语法中此结构的非终端符号。 - T.C.
@T.C. 我理解那篇文章中的一些词语。 - Jonathan Mee
显示剩余2条评论

13
这里有三个明显但相关的概念:
  1. braced-init-list: 在某些上下文中,用花括号括起来的列表所对应的语法规则。

  2. 初始化列表: list-initialization 中使用的braced-init-list初始化器的名称。

  3. std::initializer_list: 一个包装临时数组的类,在涉及到braced-init-list的某些上下文中创建。

一些例子:
//a braced-init-list and initializer list, 
//but doesn't create a std::initializer_list
int a {4}; 

//a braced-init-list and initializer list,
//creates a std::initializer_list
std::vector b {1, 2, 3};

//a braced-init-list and initializer list,
//does not create a std::initializer_list (aggregate initialization)
int c[] = {1, 2, 3};

//d is a std::initializer_list created from an initializer list
std::initializer_list d {1, 2, 3};

//e is std::initializer_list<int>
auto e = { 4 };

//f used to be a std::initializer_list<int>, but is now int after N3922
auto f { 4 };

您可能想阅读 N3922,它改变了涉及autostd::initializer_list的一些规则。


7
我认为任何时候你使用花括号创建一个列表,你都会创建一个 initializer_list

这是不正确的。

如果这不是发生的事情,那么用花括号创建的列表是什么?

struct Foo {int a; int b;};
Foo f = {10, 20};

{10, 20} 部分不是 initializer_list。它只是一种使用对象列表创建另一个对象的语法形式。

int a[] = {10, 20, 30};

再一次,它是创建数组的一种句法形式。

这种句法形式的名称为braced-init-list


同样的问题我问过@NathanOliver:“如果我这样写,auto foo = {1, 2, 3, 4, 5},那么foo的类型是什么?它保证是一个初始化列表吗?” - Jonathan Mee
1
那将会是一个std::initializer_list<int> - R Sahu

1

当使用 {} 时,有两种不同的情况:

  1. 类型为 std::initializer_list<T>,其值可以隐式转换为 T
  2. 可以用列表中的值初始化的类型。

第一种类型强制要求列表是同质的,而第二种类型则没有限制。在下面的例子中:

struct S{ 
    int a; 
    string b 
};

void f1( S s );
void f2( int i );
void f3( std::initializer_list<int> l );

f1( {1, "zhen"} ); // construct a temporal S
f2( {1} );         // construct a temporal int
f3( {1,2,3} );     // construct a temporal list of ints

函数f1和f2使用第一种类型,而f3使用第二种类型。需要注意的是,如果存在歧义,std::initializer_list将被优先选择。例如:

void f( S s );
void f( int i );
void f( std::initializer_list<int> l );

f( {1, "zhen"} ); // calls with struct S
f( {1} );         // calls with int list with one element
f( {1,2,3} );     // calls with int list with three elements

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接