我有以下代码:
struct X
{
int a, b;
};
class Y
{
public:
Y(const X& x) : x_{x} {}; // C2797 error
private:
X x_;
};
使用MSVC2013 Update 3编译,它抱怨C2797 error。如果我用parantheses替换花括号(即x_(x)
),程序将成功编译。
为什么会这样?此编译器行为是否符合C ++ 11标准?那么C ++ 14呢?
修改:为了更清楚,我不确定上面的x_{x}
是否应该根据标准调用X(std::initializer_list)
或者它是否是有效的语法致电X(const X&)
。据我所知,这是后者。我是对的吗?
答案 0 :(得分:2)
来自标准:
- 如果T是聚合,则执行聚合初始化。
[...]
- 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。如果转换任何参数需要缩小转换(见下文),则程序格式不正确。
在上面的上下文中,x_{x}
不会调用复制构造函数,因为X
是一个聚合。它将尝试聚合初始化,其中:
在MSVC中,未实现。当X
为std::string
时,MSVC似乎也无法编译,这不是聚合,因此它可能存在一些C ++ 11合规性问题。
在gcc中,它已实现,但该程序格式错误且无法编译(尝试初始化期望{int, int}
的{{1}}的聚合。
答案 1 :(得分:1)
正如您所指出的,gcc 确实知道该语法的含义,并给出了一条特定的错误消息:
cannot convert ‘const X’ to ‘int’ in initialization
那是因为花括号{}
触发列表初始化,而列表初始化为聚合类型(如果您不知道原因,则struct X
是一个聚合,只是思考"就像一个数组,它只是保存没有任何行为的东西")执行聚合初始化。聚合初始化意味着初始化程序按顺序与数据成员配对,并且任何额外的数据成员都会初始化值。
x_.a
与x
配对
x_.b
与任何内容配对,因此值已初始化
这不是你想要的,因为你不能将所有x
都粘贴到x_.a
(这就是gcc告诉你的)。你想要的是使用拷贝构造函数直接初始化,写为_x(x)
。
Visual C ++中的故事有点不同。微软工程师仍在努力增加C ++ 11支持,这是他们尚未完成的事情之一(至少在你的版本中)。编译器知道当它在ctor初始化列表中看到{}
时,这意味着列表初始化,但它不知道如何做到这一点,所以它放弃了。
特别是,它没有看到X
是聚合,将初始化程序与数据成员配对,并查明是否将x_.a
与x
配对是可能的。
当编译器告诉你"这没有实现"时,它并不意味着代码是好的,它并不意味着代码是坏的。这意味着编译代码需要尚未发布的逻辑(可能尚未编写,尚未测试,微软以外的任何人都不知道)。你的代码被运送到工厂,装在传送带上,开始向下移动装配线,然后......从带子的末端掉下来,因为处理这种代码的机器不在工厂呢。没有人知道机器是否在那里捡起它,它会将它丢弃在错误堆中或者在另一台输送机上得到它。