在相关的proposal 非静态数据成员初始化器中明确提到了这种选择背后的理由:
在 Kona 中提出的关于标识符范围的问题:
在 07 年 9 月的 Kona 会议上,核心工作组讨论期间,出现了一个关于初始化程序中标识符范围的问题。我们是否希望允许类范围具有向前查找的可能性?还是我们想要求初始化器在解析时定义良好?
需要什么:
类范围查找的动机是我们希望能够将任何东西放入非静态数据成员的初始化程序中,我们可以将其放入内存初始化程序中而不会显着改变语义(模直接初始化 vs. 直接初始化)复制初始化):
int x();
struct S {
int i;
S() : i(x()) {} // currently well-formed, uses S::x()
// ...
static int x();
};
struct T {
int i = x(); // should use T::x(), ::x() would be a surprise
// ...
static int x();
};
问题一:
不幸的是,这使得“(表达式列表)”形式的初始值设定项在解析声明时变得模棱两可:
struct S {
int i(x); // data member with initializer
// ...
static int x;
};
struct T {
int i(x); // member function declaration
// ...
typedef int x;
};
一种可能的解决方案是依赖于现有规则,即如果声明可以是对象或函数,那么它就是函数:
struct S {
int i(j); // ill-formed...parsed as a member function,
// type j looked up but not found
// ...
static int j;
};
类似的解决方案是应用另一个现有规则,目前仅在模板中使用,如果 T 可以是类型或其他东西,那么它就是其他东西;如果我们真的指的是类型,我们可以使用“typename”:
struct S {
int i(x); // unabmiguously a data member
int j(typename y); // unabmiguously a member function
};
这两种解决方案都引入了许多用户可能会误解的微妙之处(comp.lang.c++ 上关于为什么“int i();”在块范围内没有声明默认值的许多问题证明了这一点 -初始化 int)。
本文提出的解决方案是只允许“= initializer-clause”和“{ initializer-list }”形式的初始化器。这解决了大多数情况下的歧义问题,例如:
HashingFunction hash_algorithm{"MD5"};
在这里,我们不能使用 = 形式,因为 HasningFunction 的构造函数是显式的。
在特别棘手的情况下,一个类型可能不得不被提及两次。考虑:
vector<int> x = 3; // error: the constructor taking an int is explicit
vector<int> x(3); // three elements default-initialized
vector<int> x{3}; // one element with the value 3
在这种情况下,我们必须使用适当的符号在两个备选方案之间进行选择:
vector<int> x = vector<int>(3); // rather than vector<int> x(3);
vector<int> x{3}; // one element with the value 3
问题 2:
另一个问题是,由于我们建议不对初始化静态数据成员的规则进行更改,添加 static 关键字可能会使格式良好的初始化器格式不正确:
struct S {
const int i = f(); // well-formed with forward lookup
static const int j = f(); // always ill-formed for statics
// ...
constexpr static int f() { return 0; }
};
问题 3:
第三个问题是类范围查找可能会将编译时错误转变为运行时错误:
struct S {
int i = j; // ill-formed without forward lookup, undefined behavior with
int j = 3;
};
(除非被编译器捕获,否则 i 可能会被初始化为 j 的未定义值。)
提案:
CWG 在 Kona 进行了一次 6 比 3 的投票,以支持类范围查找;这就是本文的建议,非静态数据成员的初始化器仅限于“= initializer-clause”和“{ initializer-list }”形式。
我们相信:
问题 1:这个问题不会发生,因为我们没有提出 () 表示法。 = 和 {} 初始化符号不会遇到这个问题。
问题 2:添加 static 关键字会产生许多差异,这是其中最小的一个。
问题 3:这不是一个新问题,而是与构造函数初始化程序已经存在的初始化顺序问题相同。