C ++ 11删除/默认的构造函数

时间:2018-12-05 20:51:32

标签: c++ list-initialization deleted-functions defaulted-functions

对于在C ++ 11和C ++ 17中调用构造函数的方式/原因,我有些困惑。

#include <iostream>
using namespace std;

//---

template<typename T>
struct StructTest
{
public:
  const T Var = -1;

  //---

  // constexpr StructTest() = delete;  // 1
  // constexpr StructTest() = default; // 2

  // constexpr StructTest(const StructTest &Source) = delete;                  // 3
  // constexpr StructTest(const StructTest &Source) = default;                 // 4
  // constexpr StructTest(const StructTest &Source) {cout << "Copy" << endl;}; // 5
};

//---

StructTest<int> A{};
StructTest<int> A1{1};
StructTest<int> A2(A);

//---

int main(void)
{
  return(0);
};

因此,当我取消注释某些行组合(并使用clang的c ++ 17标准标志进行编译)时,我会感到困惑:

  • 1,编译。列出AA1的初始化,以及A2的默认副本构造函数
  • 2,编译。 A和列表init A1(?)的默认构造函数,以及A2的默认副本构造函数
  • 1 + 3或2 + 3,由于A2的副本构造函数已删除而无法编译
  • 1 + 4,编译。 A的默认构造函数和列表init A1(?),以及A2的默认副本构造函数
  • 2 + 4,编译。 A和列表init A1(?)的默认构造函数,以及A2的默认副本构造函数
  • 1 + 5,无法编译。说A缺少(删除)默认构造函数,并且A1没有匹配的构造函数?
  • 2 + 5,无法编译。 A1没有匹配的构造函数?

我想我大部分都明白,但是我很困惑为什么1 + 5和2 + 5编译失败。谁能解释编译器用来选择将要使用的构造函数的逻辑,以及为什么编译失败?

如果在其他情况下我认为正在调用的构造函数是错误的,您还能指出正在调用的是什么,为什么?

3 个答案:

答案 0 :(得分:9)

  

1,编译。列出A和A1的init,以及A2的默认复制构造函数

在这种情况下,您称为 List init 的实际上是aggregate initialization,因为StructTest是一个聚合。之所以允许这样做,是因为存在显式默认或已删除的构造函数仍然会使该类成为聚合类。

  

2,编译。 A的默认构造函数和列表初始化A1?,A2的默认副本构造函数

A1像1中发生的一样被聚合初始化。其余的都正确

  

1 + 3或2 + 3,由于A2的副本构造函数已删除而无法编译

这是预期的行为,因为复制构造函数被标记为已删除。

  

1 + 4,编译。 A的默认构造函数和列表初始化A1?,A2的默认副本构造函数

再次AA1的聚合初始化

  

2 + 4,编译。 A的默认构造函数和列表初始化A1?,A2的默认副本构造函数

AA1将被聚合初始化,但在按[dcl.init.aggr]/5.1初始化Var时将使用默认成员初始化器A

  

1 + 5,无法编译。说A缺少(删除)了默认构造函数,而A1没有匹配的构造函数?

5是用户提供的非默认或删除的构造函数。这意味着StructTest不再是聚合,您不能再对其进行初始化。

  

2 + 5,无法编译。没有匹配的A1构造函数?

原因与1 + 5

答案 1 :(得分:5)

(这是其他答案的附加信息)

对于C ++ 11,C ++ 14/17和C ++ 20,此代码的行为是不同的!由于 aggregate 的定义不断变化。

在C ++ 11中,该类不是集合,因为它具有括号或相等的初始化器= -1),因此情况1无法编译。

>

在C ++ 14和17中,该类是一个聚合,其他答案涵盖了这种情况。

在C ++ 20中,该类将不再是聚合,因为存在一条新规则,即任何用户声明的构造函数都将使该类不具有聚合资格;因此情况1将停止再次编译,在情况2中,StructTest<int> A1{1};由于构造函数的参数过多等原因将无法编译。

答案 2 :(得分:3)

您所说的list init实际上称为聚合初始化。在所有情况下,您的课程都是合计的,但是当您取消第5行的注释时-此时,它不再是合计的。聚合类是 all 构造函数被默认(显式或隐式)或删除的类。您只有一个非默认的,未删除的构造函数,因此,除非您取消注释,否则您的类仍然是聚合的。

考虑到这一点,您的大多数示例都围绕聚合初始化,除非您通过删除copy-constructor或添加非默认的copy-constructor来显式禁止复制,并使类变为非聚合。

有关聚合和聚合初始化的更多信息:https://en.cppreference.com/w/cpp/language/aggregate_initialization