C++ 为什么删除复制构造函数时默认初始化会失败?
有人能给我解释一下为什么下面没有编译吗?我不知道为什么编译器认为我在调用复制构造函数C++ 为什么删除复制构造函数时默认初始化会失败?,c++,c++11,C++,C++11,有人能给我解释一下为什么下面没有编译吗?我不知道为什么编译器认为我在调用复制构造函数 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失败并显示此消息(可在中复制)
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失败并显示此消息(可在中复制)
main.cpp:在函数“int main(int,char**)”中:
main.cpp:8:13:错误:调用“test::test()”时没有匹配的函数
测试arg{1};
^
main.cpp:8:13:注:候选人为:
main.cpp:4:3:注:test::test(const test&)
测试(常数测试&)=删除;
^
main.cpp:4:3:注意:参数1从'int'到'const test&'没有已知的转换
不是。错误说明:
no matching function for call to ‘test::test(<brace-enclosed initializer list>)
调用“test::test()时没有匹配的函数
这就是你想要做的
因为G++很好,所以它列出了可能的候选者,包括:
test::test(const test&) <deleted>
test::test(常量测试&)
然后它注意到,不仅删除了这个,
testarg{1}代码>将不起作用,因为无法将1
转换为test
对象。它不起作用。错误说明:
no matching function for call to ‘test::test(<brace-enclosed initializer list>)
调用“test::test()时没有匹配的函数
这就是你想要做的
因为G++很好,所以它列出了可能的候选者,包括:
test::test(const test&) <deleted>
test::test(常量测试&)
然后它注意到,不仅删除了这个,testarg{1}代码>将不起作用,因为您无法将1
转换为test
对象。这里似乎有两个问题。标题询问默认初始化,代码使用列表初始化
您依赖的是列表初始化,而不是默认初始化,特别是您正试图获得聚合初始化。第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所述,初始值设定项列表中的元素被视为聚合成员的初始值设定项,下标或成员顺序递增。
这正是您想要的,但是有一个“用户提供的构造函数”会禁用它。这与“用户声明的构造函数”不同,但一些编译器编写者可能混淆了两者(请参阅)
据我所知,对于不是聚合的类型,无法显式启用聚合初始化。但是你可以解决这个问题。将您的类型设置为聚合,并以另一种方式禁用复制构造:
struct test
{
const int index;
struct nocopy { nocopy() = default; nocopy(const nocopy&) = delete; } copy_disabled;
};
这是因为12.8p11说:
隐式声明的复制/移动构造函数是其类的内联公共成员。类X的默认复制/移动构造函数定义为已删除(8.4.3),如果X具有:
- 类类型为
M
(或其数组)的非静态数据成员无法复制/移动,因为应用于M
对应构造函数的重载解析(13.3)会导致歧义或从默认构造函数中删除或无法访问的函数
但是请注意,您不能仅仅从boost::noncopyable
继承,因为聚合不能有基类
处理默认初始化的情况要简单一些。回想一下编译器声明为默认构造函数的条件:
- 没有用户声明的构造函数
既然您已经声明了一个构造函数(并将其定义为deleted),那么您也已经摆脱了默认的构造函数
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;
}
该规则来自标准中的12.1p4:
类X
的默认构造函数是类X
的构造函数,无需参数即可调用如果类X
没有用户声明的构造函数,则没有参数的构造函数将隐式声明为默认构造函数(8.4)。隐式声明的默认构造函数是其类的内联public
成员。类X
的默认构造函数定义为删除,如果
由于默认初始化依赖于编译器声明的默认构造函数,因此只有在没有用户声明的构造函数的情况下,它才能工作
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;
}
您可以通过将默认构造函数显式声明为默认构造函数来解决此问题,如下所示:
struct test
{
const int index;
test(void) = default; // <-- ADD THIS
private:
test(const test&) = delete;
};
struct测试
{
常数整数指数;
test(void)=default;//这里似乎有两个问题。标题询问默认初始化,代码使用列表初始化
您依赖的是列表初始化,而不是默认初始化,具体而言,您正在尝试获取聚合初始化。有关规则,请参见8.5.4p3:
T
类型的对象或引用的列表初始化定义如下:
- 如果
T
是聚合,则执行聚合初始化(8.5.1)
- 否则,如果
T
是类类型,则会考虑构造函数。将枚举适用的构造函数,并通过重载解析(13.3,13.3.1.7)选择最佳构造函数。如果转换任何参数需要缩小转换(见下文)