初始化列表中元素的求值顺序

Posted

技术标签:

【中文标题】初始化列表中元素的求值顺序【英文标题】:Evaluation order of elements in an initializer list 【发布时间】:2016-05-16 13:13:25 【问题描述】:

为什么首先调用函数g()?我将g() 定义为初始化列表中的第二个元素。

以下与初始化列表相关的标准引用是否相关?

§8.5.4.4:在花括号初始化列表的初始化列表中, 初始化子句,包括任何由包扩展产生的子句 (§14.5.3),按照它们出现的顺序进行评估。

#include <iostream>
#include <vector>

int f()  std::cout << "f"; return 0;
int g()  std::cout << "g"; return 0;

void h(std::vector<int> v) 

int main() 

   h(f(), g());

输出:

gf

【问题讨论】:

您的代码不包含任何初始化列表。 C++ 中未指定函数参数的求值顺序。 所有评论和重复投票者请注意:OP 没有调用带有两个参数的函数,OP 正在调用带有 one 参数的函数,这是一个初始化的向量使用统一初始化和向量的std::initializer_list 构造函数。 你是运行了编辑帖子中的代码,还是这是之前版本的结果? 您使用的是哪个版本的 GCC? 【参考方案1】:

这不是 braced-init-list,因此该规则不适用。

[C++14: 5.17/9]:braced-init-list 可能出现在

的右侧 对标量的赋值,在这种情况下,初始化列表最多只能有一个元素。 x=v 的含义,其中T 是表达式x 的标量类型,是x=Tv 的含义。 x=的含义是x=T。 对类类型对象的赋值,在这种情况下,初始化列表作为参数传递给由重载决策(13.5.3、13.3)选择的赋值运算符函数。

【讨论】:

可能我错了,但 braced-init-list 可能会出现在许多其他情况下:例如for-range-initializer (for (for-range-declaration: braced-init-list) statement, §6.5.4), jump-statements (return braced-init-list;, §6.6), ... @manlio:嗯,好吧,我确实试图找到 this 案例没有 braced-init-list 的证据它(这将是一个更好的答案)但得出的结论是我不得不引用太多。 还考虑到std::initializer_list as function argument,尤其是this answer,参数传递应该与=初始化器具有相同的含义(两者都是复制初始化)。那么这不是复制初始化(对象 list-initialized via braced-init-list)吗? @manlio:现在没有时间调查。随意写一个答案:)【参考方案2】:

在我看来引用是相关的(编译器看到一个初始化列表):

8.5/14,16:

表单中发生的初始化

T x = a;

以及在参数传递中、函数返回、抛出异常 (15.1)、处理异常 (15.3) 和聚合成员初始化 (8.5.1) 称为复制初始化。

.

.

初始化器的语义如下[...]:如果初始化器是一个花括号初始化列表,则对象是列表初始化的(8.5.4)。

(更多细节在std::initializer_list as function argument和Folds (ish) In C++11)

此外,任何-list 都应该被排序(该标准对这一事实使用了非常强烈的措辞。另见http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1030)。

所以它是probably a GCC bug(在 gcc v4.9.0 之后修复)。

确实,尝试了各种 GCC 版本,我得到了:

GCC      with --std=c++11   without (--std=c++98)
4.7.3        fg                    gf  <-
4.8.1        fg                    gf  <-
4.8.2        fg                    gf  <-
4.9.0        fg                    gf  <-
4.9.2        fg                    fg
5.1.0        fg                    fg
5.2.0        fg                    fg
6.1.0        fg                    fg

扩展初始值设定项列表仅适用于 C++11,但 GCC 无论如何都会编译代码(带有警告,例如参见 gcc -Wall -Wextragcc -Wall -Wextra -std=c++11)。

【讨论】:

我在任何情况下都尝试过结果是相同的问题没有添加--std=c++11 @user5905343 不幸的是,gcc.godbolt.org 上缺少 gcc v4.8.4,但考虑到上述测试,这似乎是一个错误。

以上是关于初始化列表中元素的求值顺序的主要内容,如果未能解决你的问题,请参考以下文章

《Go语言精进之路》读书笔记 | 理解Go语言表达式的求值顺序

C++17 中函数指针的求值顺序

Java 表达式中子表达式的求值顺序

第九章 顺序容器

“从右到左的运算符关联性”是不是与 javaScript 中赋值运算符中的求值顺序相同

第44课 函数参数的秘密(上)