Подтвердить что ты не робот

Как инициализировать массив классов с помощью конструктора удаленных копий (С++ 11)

Существующий вопрос Почему я не могу инициализировать массив объектов, если у них есть частные конструкторы копирования? специально относится к С++ 03. Я знаю из этого вопроса, что то, что я пытаюсь сделать, не разрешено в С++ 03, но я думал, что это должно быть возможно в С++ 11

У меня есть недвижущийся класс (назовите его Child), и мне нужно инициализировать массив Child в конструкторе другого класса (назовите его Parent). Под "не движимым" я подразумеваю, что адрес дочернего объекта должен оставаться неизменным во время этого срока жизни объекта. Каков правильный способ сделать это?

С С++ 11 я пробовал следующее:

class Child
{
public:
    Child (int x) {}
    ~Child () {}

    Child (const Child &) = delete;
};

class Parent
{
public:
    Parent () : children {{5}, {7}} {}

private:
    Child children[2];
};

Этот код компилируется с помощью Clang 3.5.0, но GCC 4.9.1 жалуется, что я пытаюсь использовать конструктор удаленной копии:

test.cc: In constructor ‘Parent::Parent()’:
test.cc:13:35: error: use of deleted function ‘Child::Child(const Child&)’
     Parent () : children {{5}, {7}} {}
                                   ^
test.cc:7:5: note: declared here
     Child (const Child &) = delete;
     ^

Я читал о различии между копированием-инициализацией и прямой инициализацией (здесь и здесь, например), и я хочу избежать вызова конструктора копирования, используя прямую инициализацию. Я неправильно понимаю синтаксис? Это ошибка в GCC? Или я пытаюсь сделать это просто невозможно?

4b9b3361

Ответ 1

Я согласен с комментариями, что это кажется ошибкой GCC (сообщено как 63707).

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

Ответ 2

Я столкнулся с аналогичной проблемой, а именно, что этот код

#include <iostream>

class Widget {
public:
    Widget(int i) { std::cout << "Ctor " << i << std::endl; }

    Widget(const Widget&); // = delete;
};

int main() {
    Widget w = 123;
}

скомпилирован и дал ожидаемый результат, но после раскомментации = delete ему не удалось скомпилировать gcc-4.9.

После прочтения стандарта я верить ответ лежит во втором элементе наивысшего отступа в 8.5/16, который приведен ниже.

В основном, похоже, что компилятор концептуально хочет создать временный тип Widget и напрямую инициализировать фактический объект w из этого временного объекта через конструктор копирования. Поскольку конструктор копирования удален, компиляция прекращается. Если конструктор копирования не был удален, компилятор позже поймет, что он может отойти от копии, но это не так далеко.

Вот соответствующая часть:

[...] для [...] случаев копирования-инициализации [...] пользовательские последовательности преобразования, которые могут преобразовываться из типа источника в тип адресата [...], перечисляются, как описано в 13.3.1.4, а лучший выбирается с помощью разрешения перегрузки (13.3). [...] Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временную версию cv-unqualified типа назначения. [...] Результат вызова (который является временным для случая конструктора) затем используется для прямого инициализации, согласно вышеприведенным правилам, объекта, который является местом назначения инициализации копирования. В некоторых случаях реализации разрешается исключить копирование, присущее этой прямой инициализации, путем создания промежуточный результат непосредственно в инициализированный объект; см. 12.2, 12.8.

Но я могу ошибаться, потому что в частях [...], которые я не понял, много чего есть.