4

我对如何/为什么在 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);
};

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

  • 1、编译。列出 init 的 forAA1,以及默认的复制构造函数 forA2
  • 2、编译。A和 list init (?) 的默认构造A1函数,以及 A2 的默认复制构造函数
  • 1 + 3 或 2 + 3,编译失败,因为删除了复制构造函数A2
  • 1 + 4,编译。A和 list init A1(?) 的默认构造函数,以及A2
  • 2 + 4,编译。A和 list init (?) 的默认构造A1函数,以及 A2 的默认复制构造函数
  • 1 + 5,编译失败。说A缺少(删除)默认构造函数,并且没有匹配的构造函数A1
  • 2 + 5,编译失败。没有匹配的构造函数A1

我想我了解其中的大部分内容,但我很困惑为什么 1 + 5 和 2 + 5 编译失败。谁能解释编译器用来选择它将使用的构造函数的逻辑,以及为什么它无法编译?

如果我认为在其他情况下被调用的构造函数是错误的,你能否指出被调用的内容,为什么?

4

3 回答 3

9

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

在这种情况下,您所说的List init实际上是聚合初始化,因为StructTest它是一个聚合。这是允许的,因为显式默认或删除的构造函数的存在仍然使类成为聚合。

2、编译。A 和 list init A1? 的默认构造函数,以及 A2 的默认复制构造函数

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

1 + 3 或 2 + 3,编译失败,因为删除了 A2 的复制构造函数

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

1 + 4,编译。A 和 list init A1? 的默认构造函数,以及 A2 的默认复制构造函数

同样,聚合初始化AA1

2 + 4,编译。A 和 list init A1? 的默认构造函数,以及 A2 的默认复制构造函数

A并且A1将被聚合初始化,但它将使用每个[dcl.init.aggr]/5.1Var初始化时的默认成员初始化器A

1 + 5,编译失败。说 A 缺少(删除)默认构造函数,并且 A1 没有匹配的构造函数?

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

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

与 1 + 5 相同的原因

于 2018-12-05T21:01:26.360 回答
6

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

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

在 C++11 中,该类不是聚合,因为它有一个大括号或等号初始化器(the = -1),因此案例 1 不会编译。

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

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

于 2018-12-05T23:14:34.063 回答
3

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

考虑到这一点,您的大多数示例都围绕聚合初始化,除了通过删除复制构造函数或添加非默认复制构造函数并使类非聚合来明确禁止复制的情况。

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

于 2018-12-05T21:07:22.770 回答