2014-11-01 2 views
12

Существующий вопрос о Why can't I initialise an array of objects if they have private copy constructors? конкретно относится к C++ 03. Я знаю из этого вопроса, что то, что я пытаюсь сделать, не разрешено в C++ 03, но я думал, что это должно быть возможным в C++ 11Как инициализировать массив классов с помощью конструктора удаленных копий (C++ 11)

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

С C++ 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; 
    ^

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

+1

Кажется, что это ошибка clang, а не ошибка gcc. clang не может скомпилировать код, если вы измените его на 'children {Child {5}, Child {7}}', который должен вести себя одинаково с тем, что вы опубликовали. Обходным решением будет использование «vector» и замена объектов «Child». – Praetorian

+0

g ++ успешно работает с дочерними детьми [2] {{5}, {7}}; 'который должен быть идентичен версии, где тот же самый инициализатор встречается в списке инициализаторов ctor; оба они защищены [dcl.init.list]/3 –

+1

Чтение через разделы инициализации, этот код кажется правильным; 'children [2] = {{5}, {7}}' говорит, что 'children [0]' инициализируется копией из '{5}', т.е. это то же самое, что 'Child c = {5};', и это снова покрывается [dcl.init.list], который вызывает конструктор для 'c', который принимает' int' (без привлечения копии). –

ответ

3

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

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

+0

Иногда он также терпит неудачу без деструктора: http://stackoverflow.com/questions/31906483/class-member-array-of-objects-without-default-constructor-and-deleted-copy-cont – peku33

+0

@ peku33, да, я дал аналогичный пример в отчете об ошибке GCC. Проблема возникает с нетривиальными деструкторами, а не только с пользовательскими. –

-1

Я пришел через подобный вопрос, а именно, что этот код

#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.

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

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

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

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

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

+3

Я не думаю, что это действительно связано. 'Widget w = 123;' использует конструктор копирования, 'Widget w {123};' - прямая инициализация. –

+1

Это определенно другая ситуация. Для создания «виджета» в стеке требуется доступный деструктор, потому что он автоматически вызывается для объектов стека. Если у вас есть удаленный деструктор, вы не можете его создать в стеке. Это не связано с вопросом. –

+0

@JonathanWakely Это не деструктор, который удален в моем примере. –

Смежные вопросы