2016-08-17 7 views
6

Рассмотрим следующий код:список инициализации агрегатов: когда он может вызвать конструктор копирования?

struct A { 
    int x; 
}; 

int main() { 
    A a; 
    A b{a}; 
} 

эта программа хорошо сформированную на C++ 11 стандарта? В моей копии N3797 говорится инициализация

8.5.4 Списка [dcl.init.list]

3: Список инициализация объекта или ссылок типа T определяются следующим образом:
- Если T представляет собой совокупность, выполняется агрегатная инициализация (8.5.1).
- В противном случае, если T является специализацией std::initializer_list<E>, ...
- В противном случае, если T - тип класса, учитываются конструкторы. Соответствующие конструкторы перечислены, и лучший выбирается с использованием разрешения перегрузки. Если для преобразования любого из типов требуется сужение преобразования, программа плохо сформирована.
- В противном случае, если в списке инициализаций имеется один элемент типа E, и либо T не является ссылочным, либо ссылается на E, объект или ссылка инициализируются из этого элемента; если требуется преобразование сужения для преобразования элемента в T, программа плохо сформирована.
- В противном случае, если T является ссылочным типом, значение pr-значение, временное для ссылки на тип, на T, инициализируется или инициализируется списком-списком, в зависимости от типа инициализации для ссылки, а ссылка связанный с этим временным.
- В противном случае, если в списке инициализаторов нет элементов, объект инициализируется значением.
- В противном случае программа плохо сформирована.

Точка примера: тип является агрегатом, но инициализация списка должна вызывать конструктор копирования. На gcc 4.8 и gcc 4.9, в C++ 11 стандарта, он не:

main.cpp: In function ‘int main()’: 
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization 
    A b{a}; 
     ^

и говорит A is not convertible to int или подобное, поскольку совокупная инициализация терпит неудачу. На gcc 5.4 он отлично работает в стандарте C++ 11.

С clang Вы получаете похожие ошибки с clang-3.5, 3.6, и он начинает работать с clang-3.7.

Я понимаю, что он хорошо сформирован в стандарте C++ 14 и что он упоминается в отчете о дефектах here.

Однако, я не понимаю, почему это считается дефектом в стандарте.

Когда пишет стандарт,

"Если X, Foo инициализация выполняется. В противном случае, если Y, бар-инициализация выполняется, .... В противном случае, программа плохо сформированная.",

не означает, что если X имеет место, но foo-initialization не может быть выполнена, тогда мы должны проверить, выполняется ли Y, а затем попытаться инициализировать бар?

Это приведет к тому, что пример будет работать, поскольку при сбое инициализации агрегата мы не сопоставим std::initializer_list, а следующее условие, которое мы сопоставляем, это «T - это тип класса», а затем мы рассматриваем конструкторы.

Обратите внимание, что это делает, кажется, как она работает в этом модифицированном примере

struct A { 
    int x; 
}; 

int main() { 
    A a; 
    const A & ref; 
    A b{ref}; 
} 

Все же компиляторы относиться к этому так же, как в предыдущем примере, на C++ 11 и C++ 14 стандартов. Но похоже, что измененная формулировка из записи о дефектах CWG не относится к этому делу. Она гласит:

Если T является типом класса и список инициализатора имеет единственный элемент типа cv T или типа класса, полученный из T, объекта инициализируются из этого элемента.

http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467

Но во втором примере кода, список инициализатор технически содержит const T &. Поэтому я не вижу, как это будет работать, если после сбоя инициализации агрегата не произойдет, мы должны попытаться создать конструкторы.

Я не прав? Не предполагается ли попытка создания конструкторов после сбоя инициализации агрегата?

Вот родственный пример:

#include <iostream> 

struct B { 
    int x; 

    operator int() const { return 2; } 
}; 

int main() { 
    B b{1}; 
    B c{b}; 
    std::cout << c.x << std::endl; 
} 

В clang-3.6, gcc-4.8, gcc-4.9, он печатает 2, и в clang-3.7, gcc-5.0 печатает 1.

Предполагая, что я ошибаюсь, и в стандарте C++ 11 инициализация списка агрегатов должна быть инициализирована агрегатом, и ничего больше, пока не будет введена новая формулировка в отчете о дефектах, является ли это ошибкой, что это происходит, даже когда я выбираю -std=c++11 на новых компиляторах?

ответ

3

Когда пишет стандарт,

«Если X, Foo инициализация выполняется. В противном случае, если Y, бар инициализация выполняется ...

разве это не означает, что если X держит, но Foo инициализация не может быть выполнена , то мы должны проверить, если Y держится, а затем попытаться бар инициализацию?

No. Если X имеет место, мы выполняем foo-инициализацию. Если это не удается, программа плохо сформирована.

+0

Итак, что вы думаете о примере 'struct B'? Вы думаете, что 'gcc' и' clang' используют некоторые функции C++ 14, которые не должны быть доступны в стандарте C++ 11? –

+0

@ChrisBeck: Я думаю, они решили, что это был явный дефект в стандарте C++ 11, и они внедрили исправленную версию. Я подозреваю, что единственная причина, по которой так и не удалось «разрешить», заключалась в том, что они начали делать выпуски C++ намного быстрее, поэтому вместо исправления дефектов в старых стандартах они просто исправляют это в последнюю очередь. –

4

Когда пишет стандарт,

«Если X, Foo инициализация выполняется. В противном случае, если Y, бар-инициализация выполняется, .... В противном случае, программа плохо сформирована.»

не значит ли это, что если X имеет, но Foo инициализация не может быть выполнена, то мы должны проверить, если Y имеет место, а затем попытаться бар инициализацию?

Нет, это не . Подумайте об этом, как фактический код:

T *p = ...; 
if(p) 
{ 
    p->Something(); 
} 
else 
{ ... } 

p не NULL это не значит, что это действующий указатель либо если p указывает на разрушенный объект, p->Something() противном не заставит вас пропустить, к.. else. У вас был шанс защитить звонок в условии.

Таким образом, вы получаете неопределенное поведение.

То же самое происходит здесь. Если X, то A. Это не означает, что произойдет, если A терпит неудачу; он говорит вам сделать это. Если это невозможно сделать, вы ввернуты.

+0

Итак, вы думаете, что в моем примере с 'struct B',' gcc-5' должны вести себя так же, как 'gcc-4.8' и' gcc-4.9', когда 'std = C++ 11'? –

+0

@ChrisBeck: Это * другой вопрос. Это вопрос о том, должны ли исправления дефектов ретроактивно применяться к существующим стандартам. И ответ на этот вопрос, вообще говоря, да. Это то, что отчет о дефекте * *; это * ошибка * в стандарте. Ошибки должны быть исправлены, и вам не нужно будет говорить, что вы используете следующую версию, чтобы получить их, не так ли? –

+0

Я имею в виду, что существует разница между ошибкой в ​​реализации стандарта и дефектом в формулировке стандарта. Первое, да, это должно быть разрешено выпуском, увеличивающим только патч-уровень, в идеале. Но если я скажу 'std = C++ 11', я обычно предполагаю, что они пытаются реализовать N3797. В противном случае это означает, что я не могу точно знать, что попытается сделать компилятор, если я не прочитаю все стандарты. Или я должен сказать людям: «Вы не можете использовать clang после версии 3.6, или, к сожалению, он скомпилируется, но интилизации не будут работать, как предполагалось, и вы увидите 1 вместо 2» –

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

  • Нет связанных вопросов^_^