C++ 为什么是C++;11类内初始值设定项不能使用括号?

C++ 为什么是C++;11类内初始值设定项不能使用括号?,c++,c++11,C++,C++11,例如,我不能这样写: class A { vector<int> v(12, 1); }; class A { vector<int> v1{ 12, 1 }; vector<int> v2 = vector<int>(12, 1); }; A类 { 向量v(12,1); }; 我只能这样写: class A { vector<int> v(12, 1); }; class A { vec

例如,我不能这样写:

class A
{
    vector<int> v(12, 1);
};
class A
{
    vector<int> v1{ 12, 1 };
    vector<int> v2 = vector<int>(12, 1);
};
A类
{
向量v(12,1);
};
我只能这样写:

class A
{
    vector<int> v(12, 1);
};
class A
{
    vector<int> v1{ 12, 1 };
    vector<int> v2 = vector<int>(12, 1);
};
A类
{
向量v1{12,1};
向量v2=向量(12,1);
};

C++11语言设计中的差异是如何考虑的?

一个可能的原因是,允许使用括号将使我们很快回到原来的设计中。考虑以下两种类型:

struct foo {};
struct bar
{
  bar(foo const&) {}
};
现在,您有一个类型为
bar
的数据成员要初始化,因此将其定义为

struct A
{
  bar B(foo());
};
但是您上面所做的是声明一个名为
B
的函数,该函数按值返回一个
bar
对象,并接受一个参数,该参数是一个具有签名
foo()
的函数(返回一个
foo
,并且不接受任何参数)

根据对处理这个问题的StAdvExcel所询问的问题的数量和频率来判断,这是大多数C++程序员发现的令人惊讶和不直观的东西。添加新的括号或相等的初始化语法是避免这种模糊性的开始,并且可以从一个干净的板条开始,这可能是C++委员会选择这样做的原因。
bar B{foo{}};
bar B = foo();
上面的两行都声明了一个名为
B
的对象,其类型为
bar
,与预期的一样


除了上面的猜测,我想指出,在上面的示例中,您正在做两件截然不同的事情

vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
向量v1{12,1}; 向量v2=向量(12,1); 第一行将
v1
初始化为包含两个元素的向量,
12
1
。第二个创建一个向量
v2
,其中包含
12个
元素,每个元素都初始化为
1


请注意此规则-如果类型定义的构造函数采用
初始值设定项\u列表
,则当该类型的初始值设定项为带括号的init列表时,始终首先考虑该构造函数。只有当采用
初始值设定项列表的构造函数不可行时,才会考虑其他构造函数。

非静态数据成员初始值设定项的相关说明中明确提到了此选择背后的基本原理:

Kona提出的关于标识符范围的问题:

2007年9月在科纳举行的核心工作组会议讨论期间,出现了一个关于初始值设定项中标识符范围的问题。我们是否希望允许类范围具有向前查找的可能性;或者我们需要在解析初始值设定项时对其进行良好定义吗

需要什么:

类作用域查找的动机是,我们希望能够将任何内容放入非静态数据成员的初始值设定项中,我们可以将其放入mem初始值设定项中,而不会显著更改语义(模直接初始化与复制初始化):

问题1:

不幸的是,这使得在解析声明时“(表达式列表)”表单的初始值设定项不明确:

一种可能的解决方案是依赖现有规则,即如果声明可以是对象或函数,那么它就是函数:

类似的解决方案是应用另一个现有规则,目前只在模板中使用,即如果T可以是一个类型或其他东西,那么它就是其他东西;如果我们真的是指一种类型,我们可以使用“typename”:

这两种解决方案都引入了许多用户可能误解的细微之处(如关于COMP.Lang.c++关于“int”()的许多问题所证明的那样;“在块范围内不声明默认初始化的int)。 本文提出的解决方案是只允许“=初始值设定项子句”和“{initializer list}”形式的初始值设定项。这解决了大多数情况下的歧义问题,例如:

在这里,我们不能使用=形式,因为HasningFunction的构造函数是显式的。 在特别棘手的情况下,一种类型可能必须被提及两次。考虑:

问题3:

第三个问题是类作用域查找可能会将编译时错误转化为运行时错误:

(除非被编译器捕获,否则i可能会被未定义的值j初始化。)

提案:

CWG在科纳进行了6比3的民意测验,支持类范围查找;这就是本文提出的,非静态数据成员的初始值设定项仅限于“=初始值设定项子句”和“{initializer list}”形式。

我们认为:

问题1:这个问题不会发生,因为我们没有提出()表示法。=和{}初始值设定项符号不存在此问题

问题2:添加static关键字会产生很多差异,这是其中最小的一个

问题3:这不是一个新问题,但与构造函数初始值设定项已经存在的初始化问题顺序相同


前者调用输入为12和1的向量构造函数。后者调用向量构造函数,其输入是初始值设定项列表。它们根本不同。标准引用的原因是,语法是声明符括号或相等初始值设定项(opt)。当在参数声明中使用时,
foo()
是函数指针,而不是函数本身,就像内置的数组声明一样。@灵溪镇也不是我所说的那样。我认为逻辑不能可靠地指导C++的小细节。例如,逻辑上,由于列表初始化可以写入
v1{{12,1}
,因此可以选择
v1{12,1}
的含义来支持普通构造函数调用。这将是我作为设计师的选择,从这里的“干净板岩”开始@在你最初的陈述中
 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
    };
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; }
    };
struct S {
    int i = j; // ill-formed without forward lookup, undefined behavior with
    int j = 3;
};