我找到了一个名为Test的类,其中const int
成员名为a
:
class Test {
public:
const int a;
};
然后我实例化一个名为test的测试对象:
Test test;
并得到一个合理的编译错误"错误:'class Test'中未初始化的const成员",但是当我这样做时发生了一些奇怪的事情:
Test test = Test();
没有编译错误,发生了什么?赢了Test()
调用Test test;
之类的默认构造函数,然后调用默认的复制构造函数?我使用的编译器是" gcc(Ubuntu 4.8.4-2ubuntu1~14.04.3)4.8.4"并且编译器配置为c ++ 11模式。
答案 0 :(得分:3)
第一次初始化
Test test;
总是形象不对。简单来说,不允许你保留未初始化的const对象。
第二次初始化
Test test = Test();
在C ++ 98,C ++ 03中很好地形成,可能在C ++ 11中(但请参阅下面的 P.S。)。然而,它在C ++ 14及更高版本中是不正确的。
相关更改包含在 value-initialization 的定义中,该定义是为响应()
初始值设定项而执行的。在C ++ 03(及之前)中,缺少用户定义的默认构造函数导致 value-initialization 完全忽略构造函数,并作为独立于构造函数的初始化机制。 Test()
表达式将生成一个正确的零初始化临时对象。
但是,在C ++ 14中,一个删除的默认构造函数通过值初始化来处理与用户定义的相同:值初始化现在必须使用它,显然,失败,因为已删除。
C ++ 11 8.5 (n3242)
7 对T类型的对象进行值初始化意味着:
- 如果T是a(可能是 cv-qualified)类类型(第9条)使用用户提供的构造函数 (12.1),然后调用T的默认构造函数(和 如果T没有可访问的默认值,则初始化是错误的 构造函数);
- 如果T是(可能是cv限定的)非联合类类型 没有用户提供的构造函数,那么对象就是 零初始化,如果T是隐式声明的默认构造函数 是非平凡的,这个构造函数被调用。
...
C ++ 14 8.5 (n3690)
8 对T类型的对象进行值初始化意味着:
- 如果T是a(可能是 cv-qualified)类类型(第9节),没有默认构造函数 (12.1)或用户提供或删除的默认构造函数,然后 该对象是默认初始化的;
- 如果T是(可能是cv合格的) 类类型没有用户提供或删除的默认构造函数, 然后该对象被零初始化并且语义约束为 检查默认初始化,如果T具有非平凡的默认值 构造函数,该对象是默认初始化的;
...
(强调我的)。请注意添加"或删除"后一版本中的措辞,它将带有已删除隐式默认构造函数的类从第二个项目符号点转移到第一个项目符号。
因此,编译器在C ++ 14模式下拒绝后者的初始化代码(并在C ++ 98 / C ++ 03模式下接受它)。
换句话说,在您的情况下编译第二个初始化,因为您的编译器显然是为C ++ 03模式(或之前)配置的。
P.S。很可能(甚至很可能)我的C ++ 11草案版已经过时了。我没有C ++ 11的最终版本。 C ++ 11的最终版本可能包含与C ++ 14相同的措辞。这可以解释那些在C ++ 11模式下拒绝它的编译器的行为。在这种情况下,分隔线在C ++ 03和C ++ 11之间传递。
答案 1 :(得分:1)
Test test = Test();
没有编译错误,发生了什么?
您的编译器有错误。代码格式不正确。隐式删除了Test
的默认构造函数,因为它的格式不正确 - 您无法默认初始化const int
。它已在gcc 4.9中修复(我无法找到错误报告)。
您必须为const
成员提供初始值设定项,无论是通过Test{4}
还是通过 mem-initializer-list 。
...然后调用默认的复制构造函数?
从技术上讲,它会调用默认的移动构造函数,但无论如何都会被忽略。
答案 2 :(得分:0)
刚试过的visual studio给两个提到的东西都错了。 只需要使用成员初始化列表如下
class Test {
public:
const int a;
Test():a (0)
{
}
};
int main()
{
Test test;
Test test2 = Test();
return 0;
}