2015-04-01 5 views
14

Следующая часть кода ведет себя по-разному под g ++ 4.9.2 и clang ++ 3.7.0. Какой из них правильный? Какая часть стандарта связана с этим? Благодарю.тернарный оператор разных типов

#include <iostream> 
using namespace std; 

struct Base { 
    Base() = default; 
    Base(const Base&) = default; 
    Base(Base&&) = delete; 
}; 

struct Derived : Base { 
}; 

int main() { 
    const Base& b = true ? Derived() : Base(); 
} 

г ++ принимает его и лязг ++ выдает ошибку incompatible operand types ('Derived' and 'Base'). Подробнее см. Ниже.

[hidden]$ g++ -v 
Using built-in specs. 
COLLECT_GCC=/usr/bin/g++ 
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.9.2/lto-wrapper 
Target: x86_64-redhat-linux 
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux 
Thread model: posix 
gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) 
[hidden]$ g++ -std=c++11 b.cpp 
[hidden]$ clang++ -v 
clang version 3.7.0 (http://llvm.org/git/clang.git 6bbdbba8ec8a7730c68fee94363547dc2dc65b10) 
Target: x86_64-unknown-linux-gnu 
Thread model: posix 
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/3.4.6 
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.9.2 
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.9.2 
Candidate multilib: .;@m64 
Candidate multilib: 32;@m32 
Selected multilib: .;@m64 
[hidden]$ clang++ -std=c++11 b.cpp 
b.cpp:14:24: error: incompatible operand types ('Derived' and 'Base') 
    const Base& b = true ? Derived() : Base(); 
        ^~~~~~~~~~ ~~~~~~ 
1 error generated. 
+0

Clang может ожидать, что вы используете явное литье – fileoffset

+0

@ dyp: Крис не сказал, что для инициализации копии подразумевается использование конструктора копирования, что является ложным: если есть конструктор перемещения, конструктор копирования может не понадобиться , Он сказал, что, поскольку есть конструктор копирования, копирование-инициализация должна быть успешной, что верно, либо есть полезный конструктор перемещения, либо нет, и конструктор копирования будет использоваться. –

+0

@BenVoigt, все в порядке, я действительно не уделял достаточного внимания, когда он сказал, что инициализация копирования. В дальнейшем, это должно свести к тому, какой конструктор выбирается с помощью разрешения перегрузки для 'Base b = Derived();' – chris

ответ

6

Я не N3936 под рукой, но N3797 §5.12 [expr.cond]/3 содержит это (курсив мой):

В противном случае, если второй и третий операнд имеют различные типы и либо имеют (возможно, cv-qualit) тип класса, либо оба являются значениями той же категории значений и того же типа, за исключением cv-qualification, делается попытка конвертировать каждый из этих операндов в тип другой. Процесс для определения является ли операндом выражения Е1 типа T1 может быть преобразовано, чтобы соответствовать операнду выражения Е2 типа Т2 определяются следующим образом:

  • Если Е2 именующий: [удалено]
  • Если Е2 является xvalue: [удален]
  • Если Е2 является prvalue или если ни один из переходов выше, может быть сделано, и по крайней мере один из операндов имеет (возможно резюме квалифицированных) тип класса:
    • если Е1 и Е2 имеют класс типа, и базовые типов класса являются одинаковыми или один базовым класс другого:
      Е1 может быть преобразован, чтобы соответствовать E2, если класс Т2 одно и то же типа как, или базовый класс, класс T1 и cv-квалификация T2 - это то же самое cv-квалификация, что и более высокая квалификация , чем cv-квалификация T1. Если применяется преобразование, E1 является изменен на prvalue типа T2 на копирование-инициализация a временный тип T2 от E1 и использование этого временного в качестве преобразованного операнда.

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

Теперь, чтобы скопировать инициализировать окончательный Base операнд из Derived(), мы можем смотреть на §13.3.1.3 [over.match.ctor]:

Когда объекты типа класса являются прямым инициализируется (8.5) или копирование-инициализация из выражения того же или производного класса тип (8.5), разрешение перегрузки выбирает конструктор. Для прямой инициализации функции-кандидаты представляют собой все конструкторы класса класса инициализированного объекта. Для копирование-инициализация, функции-кандидаты - все преобразования конструкторы (12.3.1) этого класса. Список аргументов представляет собой список выражений или выражение-присваивание инициализатора.

Преобразование конструкторов определяются следующим образом в §12.3.1 [class.conv.ctor]:

Конструктор объявляется без функции спецификатора явного определяет преобразование из типов его параметров к типу его классу. Такой конструктор называется конструктором преобразования.

Теперь, если вы будете верить мне (ради не имея процитировать больше, чем я 13,3), что prvalue Derived() вызовет разрешение перегрузки выбрать конструктор перемещения (с Base&&), despite being deleted, это вызывает ошибку у Clang.

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

+0

Clang действительно пытается его переместить: http://coliru.stacked-crooked.com/a/6542febd5d884ecc – Blob

+0

Я смущен, почему здесь задействован конструктор? Что он пытается двигаться, и куда он пытается двигаться? –

+0

@ dyp, Это было бы плохо. Позволь мне вернуться и посмотреть. – chris

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