C++ 初始化列表中的隐式类型转换在一种情况下编译,但在另一种情况下不编译
我定义了以下类:C++ 初始化列表中的隐式类型转换在一种情况下编译,但在另一种情况下不编译,c++,c++11,C++,C++11,我定义了以下类: class A { int a; public: A(int _a = 0) :a(_a){} A(initializer_list<int> il) :a(il.size()){} friend A operator+(const A& a1, const A& a2); }; A operator+(const A& a1, const A& a2){ return A(); } 可以编译,但是下
class A {
int a;
public:
A(int _a = 0) :a(_a){}
A(initializer_list<int> il) :a(il.size()){}
friend A operator+(const A& a1, const A& a2);
};
A operator+(const A& a1, const A& a2){ return A(); }
可以编译,但是下面
A a;
a + {3, 4, 5};
无法编译。错误消息是“error C2059:syntax error:'{'
”和“error C2143:syntax error:missing';”在“{'
”之前
这两个客户端代码都尝试进行隐式类型转换,从初始化列表{3,4,5}
到类A
,但第一个成功,而第二个代码段失败。我不理解为什么
你能解释一下吗
我正在使用MS Visual Studio 2013更新4
[编辑]
以下是阅读评论和其他相关材料后的后续行动。我想我的问题现在可以归结为以下几点:
我知道在二进制表达式的RHS中不允许使用括号init list,在函数调用中也允许使用括号init list。问题是,我认为二进制表达式,比如说,
arg1+arg2
由编译器内部转换为函数调用操作符+(arg1,arg2)
,尤其是当arg1是类类型时。因此,在这一点上,二进制表达式和函数调用之间没有区别。因此,我能找到的唯一解释是,在这种二进制表达式到函数调用的转换之前应用了一个阻止规则,该规则特别检查第二个参数是否为braC.init列表或不。如果是,转换为等效函数调用将被禁止,并产生错误。我怀疑这些猜想是否真实,如果是,它是特定于C++标准编写的吗?谢谢所有参与我的问题的人。< /P> < P>由CLAN生成的错误信息非常清楚:
错误:不能在运算符“+”的右侧使用初始值设定项列表。
查看C++11标准,我发现(8.5.4/1[dcl.init.list]):
注意:可以使用列表初始化
- 作为变量定义中的初始值设定项(8.5)
- 作为新表达式中的初始值设定项(5.3.4)
- 在返回语句中(6.6.3)
- 作为函数参数(5.2.2)
- 作为下标(5.2.1)
- 作为构造函数调用的参数(8.5、5.2.3)
- 作为非静态数据成员的初始值设定项(9.2)
- 在mem初始值设定项中(12.6.2)
- 在作业的右侧(5.17)
因此,我猜括号内的init list只能在上面列出的情况下使用。
operator+(a,{1,2,3})
因情况4而起作用。a={1,2,3}
因最后一种情况而起作用。但是没有提及a+{1,2,3}
.So no.我试图查看标准,找出它不允许编写表达式+带括号的init list
的原因,我找到的第一条是示例中的注释
§5.1.2(4)
大括号的init列表不是表达式
并且它不满足主表达式的要求。但它可以用于后缀表达式:
像++
这样的二进制运算符期望表达式位于左侧和右侧,因此表达式+带括号的init list
不适合c++语法
但是大括号的init列表可以出现在赋值的右侧,因为它需要一个初始值设定项子句:
assignment-expression:
logical-or-expression assignment-operator initializer-clause
initializer-clause:
assignment-expression
braced-init-list
因此,您可以使用运算符+=:
A operator+=(A& a1, const A& a2){ return A(); }
int main() {
A a;
a + A{3, 4, 5};
a += {3, 4, 5};
}
这让我想起了运算符的答案:{3,4,5}不是类类型。因此没有隐式转换。语法上,
{3,4,5}
是一个大括号的初始化列表。大括号的初始化列表不是一个表达式;语法只允许它出现在有限的地方,它不能出现在整数文本可能出现的任何地方。特别是,它可以作为函数调用的参数,并显式允许出现在赋值运算符的右侧(5.18/9)这里有一个很好的答案:但是为什么我们不能将a+{1,2,3}
解释为operator+(a,{1,2,3})
并应用案例4?这个转换(从arg1+arg2
到operator+(arg1,arg2)
)是一个普通的重载运算符定义的概念。@ USE280121,我不是编译器编写者。所以我不能给你一个确切的答案。C++解析是复杂的。只需解释<代码> A+{ 1, 2, 3 } /COD> > <代码>运算符+(A,{ 1, 2, 3 })。对于C++普通用户来说,这听起来是合理的,但对于编写引擎程序的人来说,这并不是很简单。
assignment-expression:
logical-or-expression assignment-operator initializer-clause
initializer-clause:
assignment-expression
braced-init-list
A operator+=(A& a1, const A& a2){ return A(); }
int main() {
A a;
a + A{3, 4, 5};
a += {3, 4, 5};
}