在成员初始值设定项列表中递增会产生未定义的行为吗?
Does incrementing in a member initializer list generate undefined behavior?
这会导致未定义的行为吗?具体来说,初始化列表中的递增以及如何对其进行评估。
class Wrinkle {
public:
Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:
int a;
int x;
int b;
};
成员声明和初始化列表之间的顺序差异是有意为之的,因为这是一个可以准确展示差异的示例,所以请暂时忽略它。
C++17 标准包含示例 almost exactly the same as in the question:
struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };
struct D : B1, B2 {
D(int);
B1 b;
const int c;
};
D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ }
D d(10);
后面是注释:
[ Note: The initialization performed by each mem-initializer constitutes a full-expression (4.6). Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. — end note ]
在 link 之后,第 4.6 节告诉我们 definitions of "full-expression" is
之一
... a mem-initializer, including the constituent expressions of the initializer,
短语 "including constituent expressions of the initiailizer" 强烈建议我上面的代码是合法的,因为 ++i
的副作用将在继续下一个初始化程序之前完成。不过,这只是我对标准的解读,我很乐意听从任何比我有更多标准经验的人。
(还值得注意的是,成员的初始化将按照它们在 class 中声明的顺序进行,而不是按照它们在成员初始化列表中出现的顺序进行)。
这不会生成未定义的行为,因为:
[ Note: The initialization performed by each mem-initializer constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. ]
5. A full-expression is
- [...]
- an init-declarator or a mem-initializer, including the constituent expressions of the initializer,
9. Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
但要注意:
In a non-delegating constructor, initialization proceeds in the following order:
[...]
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
因此您的代码将有效地将 i + 1
分配给 a
,将 i + 2
分配给 x
,将 i + 3
分配给 b
。
这会导致未定义的行为吗?具体来说,初始化列表中的递增以及如何对其进行评估。
class Wrinkle {
public:
Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:
int a;
int x;
int b;
};
成员声明和初始化列表之间的顺序差异是有意为之的,因为这是一个可以准确展示差异的示例,所以请暂时忽略它。
C++17 标准包含示例 almost exactly the same as in the question:
struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };
struct D : B1, B2 {
D(int);
B1 b;
const int c;
};
D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ }
D d(10);
后面是注释:
[ Note: The initialization performed by each mem-initializer constitutes a full-expression (4.6). Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. — end note ]
在 link 之后,第 4.6 节告诉我们 definitions of "full-expression" is
之一... a mem-initializer, including the constituent expressions of the initializer,
短语 "including constituent expressions of the initiailizer" 强烈建议我上面的代码是合法的,因为 ++i
的副作用将在继续下一个初始化程序之前完成。不过,这只是我对标准的解读,我很乐意听从任何比我有更多标准经验的人。
(还值得注意的是,成员的初始化将按照它们在 class 中声明的顺序进行,而不是按照它们在成员初始化列表中出现的顺序进行)。
这不会生成未定义的行为,因为:
[ Note: The initialization performed by each mem-initializer constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. ]
5. A full-expression is
- [...]
- an init-declarator or a mem-initializer, including the constituent expressions of the initializer,
9. Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
但要注意:
In a non-delegating constructor, initialization proceeds in the following order:
[...]
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
因此您的代码将有效地将 i + 1
分配给 a
,将 i + 2
分配给 x
,将 i + 3
分配给 b
。