英文:
Does curly brackets initialization enforce order of evaluation of default arguments?
问题
假设有一个类 `T`,具有以下构造函数(仅此):
```lang-cpp
T::T(int one, int two) {}
花括号初始化可以确保参数的从左到右评估顺序,如下所示:
T t{ 表达式1, 表达式2 };
因此,这里始终会先评估 表达式1
,然后再评估 表达式2
。但如果 表达式2
是给定参数的默认参数,是否仍然适用相同的规则呢?
T::T(int one, int two = 表达式2) {}
....
T t{ 表达式1 };
<details>
<summary>英文:</summary>
Assume there is a class `T` with the following constructor (only):
```lang-cpp
T::T(int one, int two) {}
Curly brackets initialization helps to get left-to-right evaluation order of arguments, like so:
T t{ expression1, expression2 };
So here we get expression1
always evaluated before expression2
. But does the same rule apply in case expression2
is default argument for the given parameter?
T::T(int one, int two = expression2) {}
....
T t{ expression1 };
答案1
得分: 3
不可以假设 expression1
会在默认参数之前被评估。
确保在列表初始化中进行从左到右的评估的规则是[dcl.init.list]/4:
在大括号初始化列表内,初始化子句,包括任何由打包扩展([temp.variadic])导致的那些,按照它们出现的顺序进行评估。也就是说,与给定初始化子句相关联的每个值计算和副作用都在初始化列表的逗号分隔列表中跟随它的每个初始化子句相关联的每个值计算和副作用之前定序。[...]
在这里,初始化列表和初始化子句是语法产生物,因此清楚地表明,排序保证仅适用于初始化子句之间,即初始化列表的词法组件。 (但当然,它还扩展到初始化子句的评估中包括的任何隐式函数调用等。)
默认参数不属于初始化子句,因此它受到较弱的保证[expr.call]/7的覆盖,根据这个规定,参数 two
的初始化(包括其默认参数的评估)与每个其他参数的初始化在排序上是不确定的,也就是不确定 two
是在 one
之前初始化还是之后,并且它们不能交错。
英文:
No, you can't assume that expression1
will be evaluated before the default argument.
The rule that guarantees left-to-right evaluation in list-initialization is [dcl.init.list]/4:
> Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [...]
Here, initializer-list and initializer-clause are grammar productions, so it's clear that the sequencing guarantee only applies between the initializer-clauses, i.e., the lexical components of the initializer-list. (But it does, of course, extend to any e.g. implicit function calls that are part of the evaluation of the initializer-clauses.)
The default argument is not part of an initializer-clause so it is covered under a weaker guarantee, [expr.call]/7, according to which the initialization of the parameter two
(including the evaluation of its default argument) is indeterminately sequenced relative to the initialization of every other parameter, i.e., it's unspecified whether two
is initialized before or after one
, and they can't be interleaved.
答案2
得分: 0
是的,在给定参数的情况下,相同的规则也适用于expression2作为默认参数吗?
是的,在这里也适用相同的规则,因为调用表达式 T t{ expression1 };
和 T t{ expression1, expression2 };
是完全等价的。
[示例1:声明
void point(int = 3, int = 4);
声明了一个可以用int类型的零个、一个或两个参数调用的函数。可以以以下任何方式调用它:
point(1,2); point(1); point();
最后两个调用等同于
point(1,4)
和point(3,4)
,分别。 — 结束示例]
英文:
> But does the same rule apply in case expression2 is default argument for the given parameter?
Yes the same applies here because the call expressions T t{ expression1 };
and T t{ expression1, expression2 };
are completely equivalent.
From dcl.fct.default:
> [Example 1: The declaration
>
> void point(int = 3, int = 4);
>
> declares a function that can be called with zero, one, or two arguments of type int. It can be called in any of these ways:
>
> point(1,2); point(1); point();
>
> The last two calls are equivalent to point(1,4)
and point(3,4)
, respectively.
— end example]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论