有人可以向我解释为什么以下不编译?我不确定编译器为什么认为我正在调用复制构造函数。
struct test {
const int index;
private:
test(const test&) = delete; // comment out this line and voila.
};
int main(int argc, char** argv) {
test arg{1};
return arg.index;
}
GCC因此消息失败(可在http://www.compileonline.com/compile_cpp11_online.php中重现)
main.cpp: In function ‘int main(int, char**)’:
main.cpp:8:13: error: no matching function for call to ‘test::test(<brace-enclosed initializer list>)’
test arg{1};
^
main.cpp:8:13: note: candidate is:
main.cpp:4:3: note: test::test(const test&) <deleted>
test(const test&) = delete;
^
main.cpp:4:3: note: no known conversion for argument 1 from ‘int’ to ‘const test&’
答案 0 :(得分:6)
这里似乎有两个问题。标题询问 default-initialization ,您的代码使用 list-initialization 。
您依赖于列表初始化,而不是默认初始化,特别是您正在尝试进行聚合初始化。其规则见8.5.4p3:
类型
T
的对象或引用的列表初始化定义如下:
- 如果
T
是聚合,则执行聚合初始化(8.5.1)。...
- 否则,如果
T
是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。如果转换任何参数需要缩小转换(见下文),则程序格式不正确。
和8.5.1:
聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚拟功能(10.3)。
当初始化列表初始化聚合时,如8.5.4中所述,初始化列表的元素被视为聚合成员的初始化者,增加下标或成员顺序。
这正是你想要的,但拥有一个用户提供的构造函数&#34;禁用它。这与&#34;用户声明的构造函数&#34;不同,但是一些编译器编写者可能会混淆这两者(参见@dyp's answer)。
据我所知,没有办法为不是聚合的类型显式启用聚合初始化。但是你可以解决这个问题。使您的类型成为聚合,并以另一种方式禁用复制构造:
struct test
{
const int index;
struct nocopy { nocopy() = default; nocopy(const nocopy&) = delete; } copy_disabled;
};
这是有效的,因为12.8p11说:
隐式声明的复制/移动构造函数是其类的内联公共成员。如果X具有以下内容,则将类X的默认复制/移动构造函数定义为已删除(8.4.3):
...
- 类型
M
(或其数组)的非静态数据成员,由于重载决策(13.3)无法复制/移动,因为应用于M
的相应构造函数,导致模糊或从默认构造函数中删除或无法访问的函数,
但请注意,您不能继承boost::noncopyable
,因为聚合不能包含基类。
处理默认初始化的情况稍微容易一些。回想一下编译器声明的默认构造函数的条件:
由于您已经声明了构造函数(并将其定义为已删除),因此您还可以删除默认的默认构造函数。
此规则来自标准中的12.1p4:
类
X
的默认构造函数是类X
的构造函数,可以在没有参数的情况下调用它。 如果类X
没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认(8.4)。隐式声明的默认构造函数是其类的inline public
成员。如果...... {/ p>,则将类X
的默认默认构造函数定义为已删除
由于默认初始化依赖于编译器声明的默认构造函数,因此当且仅当没有用户声明的构造函数时,它才有效。
您可以通过将默认构造函数显式声明为默认值来解决此问题,如下所示:
struct test
{
const int index;
test(void) = default; // <-- ADD THIS
private:
test(const test&) = delete;
};
但是这在你的情况下不起作用,默认的默认构造函数被删除,因为规则继续
如果......
,则将类X的默认默认构造函数定义为已删除
const
的任何非变体非静态数据成员 - 没有 brace-or-equal-initializer 的限定类型(或其数组)没有用户提供的默认值构造,
答案 1 :(得分:3)
这是一个gcc错误,请参阅Bug 52707 - C++11 Deleted special member function prevent type being an aggregate 。 gcc错误地认为该类不再是聚合,如果它具有已删除的特殊成员函数(如删除的复制构造函数)。
这似乎已经在g ++ 4.9.0中修复了
答案 2 :(得分:1)
不是。该错误表明:
no matching function for call to ‘test::test(<brace-enclosed initializer list>)
这是你想要做的。
因为G ++很好,它列出了可能的候选者,其中包括:
test::test(const test&) <deleted>
然后它会注意到,这不仅会被删除,test arg{1};
将无效,因为您无法将1
转换为test
个对象。