C ++ 11 удаленные / дефолтные конструкторы

Я немного озадачен тем, как / почему конструкторы вызываются в 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, компилирует. Список инициалов для A а также A1и конструктор копирования по умолчанию для 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 не удалась. Может ли кто-нибудь объяснить логику, которую использует компилятор для выбора конструкторов, которые он собирается использовать, и почему он не скомпилируется?

Если конструкторы, которые, как мне кажется, вызываются в других случаях, неверны, не могли бы вы также указать, что вызывается и почему?

4

Решение

1, компилирует. Перечислите инициалы для A и A1 и конструктор копирования по умолчанию для A2

Что ты называешь Список инициалов в этом случае на самом деле агрегатная инициализация так как StructTest это совокупность. Это разрешено, потому что наличие явно дефолтного или удаленного конструктора все еще делает класс агрегатным.

2, Компилирует. Конструктор по умолчанию для A и списка init A1? И конструктор копирования по умолчанию для A2

A1 инициализируется агрегат, как то, что произошло в 1. Остальное правильно

1 + 3 или 2 + 3, не компилируется из-за удаленного конструктора копирования для A2

Это ожидаемое поведение, поскольку конструктор копирования помечается как удаленный.

1 + 4, компилируется. Конструктор по умолчанию для A и списка init A1? И конструктор копирования по умолчанию для A2

Опять же, совокупная инициализация для A а также A1

2 + 4, компилируется. Конструктор по умолчанию для A и списка init A1? И конструктор копирования по умолчанию для A2

A а также A1 будет инициализирован агрегатом, но будет использовать инициализатор члена по умолчанию Var при инициализации A в [Dcl.init.aggr] /5.1

1 + 5, не компилируется. Говорит, что отсутствует (удален) конструктор по умолчанию, и нет подходящего конструктора для A1?

5 — предоставленный пользователем конструктор по умолчанию или удаленный. Это означает StructTest больше не является агрегатом, и вы больше не можете его инициализировать.

2 + 5, не компилируется. Нет подходящего конструктора для A1?

Та же причина, что и 1 + 5

9

Другие решения

(Это дополнительная информация к другим ответам)

Поведение этого кода отличается для C ++ 11, C ++ 14/17 и C ++ 20! В связи с изменением определения совокупный.

В C ++ 11 класс не был агрегатом, потому что он имеет скобки или равно-инициализатор ( = -1), поэтому случай 1 не будет компилироваться.

В C ++ 14 и 17 класс является агрегатом, остальные ответы охватывают этот случай.

В C ++ 20 класс не будет снова агрегатом, потому что есть новое правило, согласно которому любой объявленный пользователем конструктор лишает класс возможности быть агрегатом; поэтому случай 1 перестанет компилироваться снова, а в случае 2, StructTest<int> A1{1}; не будет компилироваться из-за слишком большого количества аргументов конструктора и т. д.

5

Что вы называете list init фактически называется агрегатной инициализацией. Ваш класс является агрегатом во всех случаях, но когда вы раскомментируете строку 5 — в этот момент он перестает быть агрегатом. Агрегатный класс — это класс, в котором все конструкторы либо по умолчанию (явно или неявно), либо удалены. У вас есть только один не дефолтный, не удаленный конструктор, поэтому, если вы не раскомментируете это, ваш класс останется агрегатом.

Имея это в виду, большая часть вашего примера вращается вокруг инициализации агрегата, за исключением случая, когда вы явно запрещаете копирование, удаляя конструктор копирования или добавляя конструктор копирования без дефолтов и делая класс неагрегированным.

Подробнее о агрегатной и агрегатной инициализации: https://en.cppreference.com/w/cpp/language/aggregate_initialization

3
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector