有人可以向我解释为什么下面的代码无法编译吗?我不确定为什么编译器认为我正在调用复制构造函数。
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&’
最佳答案
您似乎有两个问题。标题询问默认初始化,而您的代码使用列表初始化。
您依赖于列表初始化,而不是默认初始化,具体来说,您正在尝试进行聚合初始化。相关规则可在 8.5.4p3 中找到:
List-initialization of an object or reference of type
T
is defined as follows:
- If
T
is an aggregate, aggregate initialization is performed (8.5.1)....
- Otherwise, if
T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
在 8.5.1 中:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order.
这正是您想要的,但是拥有“用户提供的构造函数”会禁用它。这与“用户声明的构造函数”不同,但一些编译器作者可能混淆了两者(参见 @dyp's answer)。
据我所知,没有办法为不是聚合的类型显式启用聚合初始化。但是您可以解决这个问题。使您的类型成为聚合类型,并以另一种方式禁用复制构造:
struct test
{
const int index;
struct nocopy { nocopy() = default; nocopy(const nocopy&) = delete; } copy_disabled;
};
这是有效的,因为 12.8p11 说:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
...
- a non-static data member of class type
M
(or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied toM
's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
但是请注意,您不能只继承 boost::noncopyable
,因为聚合不能有基类。
处理默认初始化的情况要简单一些。记忆一下拥有编译器声明的默认构造函数的条件:
- 没有用户声明的构造函数。
由于您已经声明了一个构造函数(并将其定义为已删除),您还摆脱了默认的默认构造函数。
这条规则来自标准中的 12.1p4:
A default constructor for a class
X
is a constructor of classX
that can be called without an argument. If there is no user-declared constructor for classX
, a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is aninline public
member of its class. A defaulted default constructor for classX
is defined as deleted if ...
由于默认初始化依赖于编译器声明的默认构造函数,因此当且仅当没有用户声明的构造函数时它才有效。
您可以通过将默认构造函数显式声明为默认构造函数来解决此问题,如下所示:
struct test
{
const int index;
test(void) = default; // <-- ADD THIS
private:
test(const test&) = delete;
};
但这在你的情况下不起作用,默认的默认构造函数被删除,因为规则继续
A defaulted default constructor for class X is defined as deleted if ...
- any non-variant non-static data member of
const
-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
关于c++ - 为什么删除复制构造函数时默认初始化会失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23304025/