C++11 error: use of deleted function
От: Elija  
Дата: 07.04.16 10:35
Оценка:
У меня два компилятора:
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Microsoft Visual Studio 2015 ver. 14.0. (Visual C++ 2015)

Просто код для примера:

#include <queue>

class C
{
};


class B
{
public:
  // assignment and copy prohibited
  B(const B&) = delete;
  B& operator=(const B&) = delete;

  B(int v1, int v2) : m_V1(v1), m_V2(v2) {}

private:
  int m_V1;
  int m_V2;
  std::queue<C> m_Queue;
};

class A
{
public:
  // assignment and copy prohibited
  A(const A&) = delete;
  A& operator=(const A&) = delete;

  A(int p1, int p2) : arrB{ {p1+1, p2+2}, {p1+3, p2+4}, {p1+5, p2+6} } { }

private:
  B arrB[3];
};


1) Если я использую g++ и

std::queue<C> m_Queue;


то я получаю следующую ошибку:

make
g++ -std=c++11 -c test.cpp
test.cpp: In constructor ‘A::A(int, int)’:
test.cpp:29:70: error: use of deleted function ‘B::B(const B&)’
   A(int p1, int p2) : arrB{ {p1+1, p2+2}, {p1+3, p2+4}, {p1+5, p2+6} } { }
                                                                      ^
test.cpp:11:3: error: declared here
   B(const B&) = delete;
   ^
make: *** [test.o] Error 1


2) Если же я использую тот же g++, но комментирую линию кода

//std::queue<C> m_Queue;


или если я использую оба варианта (раскомментировано и закомментировано) с Microsoft Visual Studio 2015, то я не получаю никаких ошибок.

3) Как я понимаю, инициализация списком использует или direct-list-initialization или copy-list-initialization.

8.5.4 List-initialization
1 List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copyinitialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.

Но, видимо, я не понимаю какой из видов инициализации будет использован в моём случае. Я предполагал, что должна использоваться direct-list-initialization в соответствии с http://en.cppreference.com/w/cpp/language/list_initialization (вариант (5) по этой ссылке, вариант (9) — не то, поскольку это "a user-defined operator[]"). Но тогда причём тут недоступный конструктор копирования? И какая связь с наличием экземпляра очереди в классе?

Может мне кто-то подробно объяснить эту ситуацию и в чём я ошибаюсь и какой компилятор прав?
Re: C++11 error: use of deleted function
От: dead0k  
Дата: 07.04.16 10:43
Оценка:
Здравствуйте, Elija, Вы писали:

E>какая связь с наличием экземпляра очереди в классе?

Думаю, наличие нетривиального члена запрещает создание move-конструктора
Re[2]: C++11 error: use of deleted function
От: Elija  
Дата: 07.04.16 10:53
Оценка:
Здравствуйте, dead0k, Вы писали:

E>>какая связь с наличием экземпляра очереди в классе?

D>Думаю, наличие нетривиального члена запрещает создание move-конструктора

А почему там должен использоваться конструктор перемещения? Если можно поподробнее со ссылкой на источник.
И это не отменяет вопроса почему g++ жалуется на недоступный конструктор копирования и кто из компиляторов прав...
Re: C++11 error: use of deleted function
От: Кодт Россия  
Дата: 07.04.16 11:21
Оценка:
Здравствуйте, Elija, Вы писали:

Критичным для gcc является нетривиальный деструктор у B — из-за нетривиального деструктора у queue<C>.
Можно упростить:
struct B {
  B(int,int) {}
  B(const B&) = delete;
  ~B() {}
};

и получить то же самое.

По всей видимости, VC просто срезал угол: выполнил copy elision.
Перекуём баги на фичи!
Отредактировано 07.04.2016 11:23 Кодт . Предыдущая версия .
Re[2]: C++11 error: use of deleted function
От: Elija  
Дата: 07.04.16 11:37
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Elija, Вы писали:


К>Критичным для gcc является нетривиальный деструктор у B — из-за нетривиального деструктора у queue<C>.

К>Можно упростить:
К>
К>struct B {
К>  B(int,int) {}
К>  B(const B&) = delete;
К>  ~B() {}
К>};
К>

К>и получить то же самое.

К>По всей видимости, VC просто срезал угол: выполнил copy elision.


А как это связано с инициализацией массива? Потому что, если заменить массив
B arrB[3];

на один экземпляр
B instB;

и соответственно провести инициализацию в конструкторе вместо
arrB{ {p1+1, p2+2}, {p1+3, p2+4}, {p1+5, p2+6} }

как
instB {p1+1, p2+2}

то ошибка исчезает.
Что-то я не понимаю...
Re: C++11 error: use of deleted function
От: watchmaker  
Дата: 07.04.16 11:39
Оценка: 1 (1)
Здравствуйте, Elija, Вы писали:

E>3) Как я понимаю, инициализация списком использует или direct-list-initialization или copy-list-initialization.


E>8.5.4 List-initialization

Это не то. Инициализация массивов описана в dcl.init.aggr. В принципе там описано, что если элементы массива не агрегаты, то для них будет выполняться copy-initialized from the corresponding initializer-clause. То есть компилятор может сначала сконструировать элементы типа B, а потом их скопировать в arrB (на самом деле не может
Автор: watchmaker
Дата: 08.04.16
), что без конструктора копирования или конструктора перемещения сделать нельзя. Конструктор копирования у тебя удалён, а вот добавление перемещающего спасает дело.

Ну и как уже заметили, автогенерация конструкторов и copy ellision просто всё запутывают.

Обойти можно избавившись от агрегата:
  //B arrB[3];
  std::array<B, 3> arrB;

A(int p1, int p2)
    : arrB{{ {p1+1, p2+2}, {p1+3, p2+4}, {p1+5, p2+6} }}
  { }
Отредактировано 08.04.2016 1:42 watchmaker . Предыдущая версия .
Re[2]: C++11 error: use of deleted function
От: Elija  
Дата: 07.04.16 11:47
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, Elija, Вы писали:


E>>3) Как я понимаю, инициализация списком использует или direct-list-initialization или copy-list-initialization.


E>>8.5.4 List-initialization

W>Это не то. Инициализация массивов описана в dcl.init.aggr. В принципе там описано, что если элементы массива не агрегаты, то для них будет выполняться copy-initialized from the corresponding initializer-clause. То есть компилятор может сначала сконструировать элементы типа B, а потом их скопировать в arrB, что без конструктора копирования или конструктора перемещения сделать нельзя. Конструктор копирования у тебя удалён, а вот добавление перемещающего спасает дело.

W>Ну и как уже заметили, автогенерация конструкторов и copy ellision просто всё запутывают.


W>Короче, избавься от агрегата:

W>
W>  //B arrB[3];
W>  std::array<B, 3> arrB;
W>

W>
W>A(int p1, int p2)
W>    : arrB{{ {p1+1, p2+2}, {p1+3, p2+4}, {p1+5, p2+6} }}
W>  { }
W>



Спасибо! Значит был не прав в отношении применения List-initialization к массивам. Остальное понятно. Ещё раз спасибо за пояснения
Re[2]: C++11 error: use of deleted function
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 07.04.16 11:52
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, Elija, Вы писали:


E>>3) Как я понимаю, инициализация списком использует или direct-list-initialization или copy-list-initialization.


E>>8.5.4 List-initialization

W> Конструктор копирования у тебя удалён, а вот добавление перемещающего спасает дело.
Получается, добавив один конструктор копирования руками и пометив его как delete мы отключаем автогенерацию конструктора перемещения?
Sic luceat lux!
Re[2]: C++11 error: use of deleted function
От: dead0k  
Дата: 07.04.16 11:55
Оценка:
Здравствуйте, watchmaker, Вы писали:
W>Конструктор копирования у тебя удалён, а вот добавление перемещающего спасает дело.

Похоже, что есть что-то еще. Я запретил все конструкторы копирования/операторы присваивания, но не смог добиться ошибки компиляции в случае тривиального класса (без очереди):
http://ideone.com/ieCuiI
Отредактировано 07.04.2016 12:00 dead0k . Предыдущая версия .
Re[3]: C++11 error: use of deleted function
От: B0FEE664  
Дата: 07.04.16 12:18
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Получается, добавив один конструктор копирования руками и пометив его как delete мы отключаем автогенерацию конструктора перемещения?


Конструктор перемещения можно вернуть написав: " = default;":
  B(B&&) = default;
И каждый день — без права на ошибку...
Re[3]: C++11 error: use of deleted function
От: Elija  
Дата: 07.04.16 12:40
Оценка:
Здравствуйте, dead0k, Вы писали:

D>Здравствуйте, watchmaker, Вы писали:

W>>Конструктор копирования у тебя удалён, а вот добавление перемещающего спасает дело.

D>Похоже, что есть что-то еще. Я запретил все конструкторы копирования/операторы присваивания, но не смог добиться ошибки компиляции в случае тривиального класса (без очереди):

D>http://ideone.com/ieCuiI

Да, опять непонятно... То есть в каких-то случаях элементы массива copy-initialized from the corresponding initializer-clause, а в каких-то нет? И что это за случаи?
Re[2]: C++11 error: use of deleted function
От: Erop Россия  
Дата: 07.04.16 15:52
Оценка:
Здравствуйте, dead0k, Вы писали:

D>Думаю, наличие нетривиального члена запрещает создание move-конструктора

А разве у std::deque нет move-конструктора?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: C++11 error: use of deleted function
От: watchmaker  
Дата: 08.04.16 01:38
Оценка:
Здравствуйте, Elija, Вы писали:

E>Да, опять непонятно... То есть в каких-то случаях элементы массива copy-initialized from the corresponding initializer-clause, а в каких-то нет?


Выше я неверно написал. Там конечно хоть и происходит copy-initialization, но по стандарту copy-initialization должен успешно срабатывать если доступен не explicit конструктор с аргументами, принимающими соответствующий expression-list (а наличие или отсутствие конструктора копирования после этого уже не важно). То есть твой код валиден, а ошибка в gcc. Легко тригеррится, если класс не trivially destructible (а включение std::queue к этому тоже приводит). Ну а обход проблемы через std::array работает по прежнему из-за того, что там нет агрегатной инициализации.
Re[2]: C++11 error: use of deleted function
От: T4r4sB Россия  
Дата: 08.04.16 07:43
Оценка:
Здравствуйте, watchmaker, Вы писали:

W> В принципе там описано, что если элементы массива не агрегаты, то для них будет выполняться copy-initialized from the corresponding initializer-clause.


Упорото как-то, почему на месте собрать элементы нельзя?

W>Обойти можно избавившись от агрегата:

W> std::array<B, 3> arrB;

Хм, вот ещё одно преимущество защищённого массива перед сырым. Правда, я уже не понимаю, почему так, внутри-то там тот же сырой массив в привате.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[3]: C++11 error: use of deleted function
От: watchmaker  
Дата: 08.04.16 08:43
Оценка:
Здравствуйте, T4r4sB, Вы писали:

W>> В принципе там описано, что если элементы массива не агрегаты, то для них будет выполняться copy-initialized from the corresponding initializer-clause.


TB>Упорото как-то, почему на месте собрать элементы нельзя?


Называется оно упорото: по факту оказывается, что copy-initialization совсем не запрещает делать инициализацию по месту, а наоборот — в приведённом коде copy-initialization не должно делать никаких копирований даже несмотря на наличие copy в названии.
Re[5]: C++11 error: use of deleted function
От: watchmaker  
Дата: 08.04.16 11:12
Оценка: 1 (1) +1
W> а ошибка в gcc

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63707
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.