c++ - 为什么删除复制构造函数时默认初始化会失败?

标签 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 失败并显示此消息(可在 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 to M'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 class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X 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/

相关文章:

c++ - 如何在c中调用一个dll文件。我想向它传输一个xml文件

c++ - 一个 lambda 从本质上来说,关闭自己是否有效?

c++ - Gtest 新关键字

c++ - 使用 lambda 的 Boost 算法无法编译

c++ - 我可以假设分配器不直接持有它们的内存池(因此可以被复制)吗?

c++ - 使用 XML 模式类型的 Qt GUI 输入验证

c++ - vs2010中的 vector 初始化

c++ - OSX 上的 GCC 5、6 和 7 不支持 uint

pointers - C++ 11-一直使用nullptr吗?

c++ - 成员函数模板推导或其他方法让编译器知道如何调用函数