For example, I cannot write this:
class A
{
vector<int> v(12, 1);
};
我只能写这个:
class A
{
vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
};
为什么这两种声明语法会有差异?
For example, I cannot write this:
class A
{
vector<int> v(12, 1);
};
我只能写这个:
class A
{
vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
};
为什么这两种声明语法会有差异?
Kona 中提出的一个问题是标识符的作用域:
在 2007 年 9 月在 Kona 召开的核心工作组讨论中,出现了一个关于初始化器中标识符作用域的问题。我们是否希望允许类作用域,以便进行前向查找;或者我们要求初始化器在解析时定义良好?
所需的:
使用类作用域查找的动机在于,我们希望能够将任何放入成员初始化程序的内容也放入非静态数据成员的初始化器中,而不会显著改变语义(除了直接初始化和复制初始化的区别):
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();
};
问题 1:
不幸的是,这使得“(表达式列表)”形式的初始化在解析声明时变得模糊不清:
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;
};
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"};
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
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;
};
(除非被编译器捕获,否则我可能会用j的未定义值进行初始化。)
提案:
CWG在Kona进行了6比3的稻草投票,赞成类作用域查找;这就是本文所提出的,对于非静态数据成员的初始值设定项仅限于“=初始化程序”和“{初始化列表}”形式。
我们认为:
问题1:我们不提议使用()符号,因此不会出现这个问题。=和{}初始化符号不会受到这个问题的困扰。
问题2:添加static关键字会带来许多差异,这只是其中最小的一个。
问题3:这不是一个新问题,而是已经存在于构造函数初始化程序中的初始化顺序问题。
可能的一个原因是允许括号会很快导致我们回到最令人烦恼的解析。考虑下面的两种类型:
struct foo {};
struct bar
{
bar(foo const&) {}
};
bar
的数据成员需要初始化,因此您将其定义为struct A
{
bar B(foo());
};
B
的函数,它通过值返回一个bar
对象,并接受一个单参数,该参数是一个具有签名foo()
(返回一个foo
并且不带任何参数)的函数。bar B{foo{}};
bar B = foo();
上述两行声明了一个名为B
的bar
类型对象,正如预期。
除了上面的猜测之外,我想指出你在上面的例子中做了两件截然不同的事情。
vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
v1
to a vector that contains two elements, 12
and 1
. The second creates a vector v2
that contains 12
elements, each initialized to 1
.initializer_list<T>
的构造函数,那么当类型的初始化器是一个 花括号初始化列表 时,该构造函数总是被首先考虑。只有在不可行时才会考虑其他构造函数。foo()
是一个函数指针而不是一个函数本身,就像内置数组声明一样。 - Lingxi