初始化列表中的隐式类型转换在一种情况下编译,但在另一种情况下不编译

时间:2015-04-25 04:21:28

标签: 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(); }

以下客户端代码

A a;
operator+(a,{3, 4, 5});

可以编译,但以下

A a;
a + {3, 4, 5};

无法编译。错误消息是&#34; error C2059: syntax error : '{'&#34;和&#34; error C2143: syntax error : missing ';' before '{'&#34;。

两个客户端代码都尝试执行隐式类型转换,从初始化列表{3,4,5}class A,但第一个成功完成,而第二个代码段失败。我不明白为什么。

你能解释一下吗?

我正在使用MS Visual Studio 2013 Update 4。

[编辑] 在阅读评论和其他相关材料后,这是一个跟进。我想我的问题现在可以简化为以下几点: 我知道在二进制表达式的RHS中不允许使用brace-init-list,并且在函数调用中允许它。问题是,我认为二进制表达式,例如arg1 + arg2由编译器在内部转换为函数调用operator+ (arg1, arg2),尤其是当arg1是类类型时。所以在这一点上,二进制表达式和函数调用之间没有区别。因此,我能解释的唯一解释是在这样的二元表达式到函数调用转换之前应用了一个阻止规则,它特别检查第二个参数是否是一个brace-init-list。如果是,则禁止转换为等效函数调用并产生错误。我想知道这些猜想是否真实,如果是,它是否专门用于C ++标准?感谢参与我的问题的所有人。

2 个答案:

答案 0 :(得分:3)

clang生成的错误消息非常清楚:

error: initializer list cannot be used on the right hand side of operator '+'

查看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-initializer(12.6.2)
  • 中   
  • 位于作业的右侧(5.17)
  •   

所以我猜braced-init-list只能在上面列出的情况下使用。 operator+(a, {1, 2, 3})因案例4而起作用。a = {1, 2, 3}因最后一例而起作用。但没有提及a + {1, 2, 3}。所以没有。

答案 1 :(得分:0)

我试图查看标准,找出不允许写expression + braced-init-list的原因,我发现的第一个是示例中的注释

§5.1.2(4)

  

braced-init-list不是表达式

并且它不符合primary-expression的要求。但它可以用在后缀表达式中:

postfix-expression:
    simple-type-specifier braced-init-list
    typename-specifier braced-init-list

+这样的二元运算符需要左右表达式,因此expression + braced-init-list不适合c ++ - grammer。

但是,一个braced-init-list可以出现在赋值的右侧,因为它需要一个初始化子句:

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};
}