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

5
为什么函数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

4
你的代码中没有包含任何初始化列表。 - Kerrek SB
3
C++ 中函数参数的求值顺序是未指定的。 - 101010
6
注意:所有评论和重复投票者请注意:原帖并不是调用一个带有两个参数的函数,而是调用一个带有一个参数的函数,这个参数是通过统一初始化和向量的std::initializer_list构造函数进行初始化的。 - Some programmer dude
1
你运行了编辑后的帖子中的代码吗?还是这是之前版本的结果? - interjay
2
你使用的是哪个确切版本的GCC? - Zdeslav Vojkovic
显示剩余21条评论
2个回答

3

这不是一个花括号初始化列表,因此该规则不适用。

[C++14: 5.17/9]: 花括号初始化列表可以出现在右侧:

  • 标量值的赋值语句中,此时初始化列表最多只能有一个元素。当表达式x的标量类型为T时,x={v}的含义实际上是x=T{v}。当x={}时,其含义为x=T{}
  • 类类型对象的赋值语句中,此时初始化列表作为参数传递给重载决议(13.5.3, 13.3)所选择的赋值运算符函数。

也许我错了,但是花括号初始化列表可以出现在许多其他上下文中:例如范围for循环初始化器(for (for-range-declaration: braced-init-list) statement, §6.5.4),跳转语句(return braced-init-list;, §6.6),... - manlio
@manlio:嗯,好吧,我确实尝试找到证据证明这个案例中没有使用“花括号初始化列表”(这将是一个更好的答案),但我得出结论需要引用太多内容。 - Lightness Races in Orbit
还考虑std::initializer_list作为函数参数,特别是这个答案,参数传递应该具有与=初始化程序相同的含义(两者都是复制初始化)。那么这不是复制初始化(通过花括号初始化列表初始化对象)吗? - manlio
@manlio:现在没有时间调查。随意写一个答案 :) - Lightness Races in Orbit

1

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

8.5/14,16:

在形式为

T x = a;

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

.

.

初始化程序的语义如下[...]:如果初始化程序是花括号初始化列表,则对象将进行列表初始化(8.5.4)。

(更多细节请参见std::initializer_list as function argumentFolds (ish) In C++11

此外,任何{}列表都应该被排序(标准对这一事实使用了非常强烈的措辞。请参见http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1030)。
所以这可能是GCC的一个错误(在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。 - Adib
很遗憾,https://gcc.godbolt.org 上缺少gcc v4.8.4,但考虑到上述测试结果,这似乎是一个错误。 - manlio

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