2015-04-01 152 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(); 
} 

g ++接受它並且clang ++給出錯誤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 2015-04-01 01:09:21

+0

@dyp:Chris沒有說複製初始化意味着使用複製構造函數,該複製構造函數是false:如果存在移動構造函數,則可能不需要複製構造函數。他說由於有一個拷貝構造函數,所以拷貝初始化應該成功,這是真的,要麼有一個可用的移動構造函數,要麼沒有拷貝構造函數被使用。 – 2015-04-01 01:32:23

+0

@BenVoigt,沒關係,當它說複製初始化時,我真的沒有足夠的注意力。進一步看,它應該歸結爲哪個構造函數是通過重載分辨率挑選出來的?Base b = Derived();' – chris 2015-04-01 01:37:55

回答

6

我沒有N3936方便,但N3797§5.12[expr.cond/3包含此(重點煤礦):

否則,如果第二個和第三個操作數有不同的類型和 或者具有(可能是CV-合格的)類別類型,或者如果兩者都是具有相同值類別的glvalues 和除了 cv-qualification之外的相同類型,則嘗試將這些操作數 中的每一個轉換爲另一個。用於確定類型T1的 操作數表達E1是否可以轉換,以匹配類型T2的操作數 表達E2的定義如下的方法:

  • 如果E2是一個左值:[移除]
  • 如果E2是一個x值:[移除]
  • 如果E2是prvalue或者如果操作數既不轉化 以上可以做到和中的至少一個具有(可能 CV修飾)類類型:
    • 如果E1和E2具有類類型和 底層類類型相同或一個是基類 其他的:
      E1可以被轉換以匹配E2如果類T2是相同的 類型如T1,的基類,T1的類別和T2的cv資格 與T1的cv限定相同,或更大的cv資格 。如果應用了轉換,則E1將 更改爲類型T2的前值複製初始化從E1中暫時變爲 類型T2並將該臨時值用作轉換後的操作數。

使用這個過程,則確定所述第二操作數是否可以是 轉換以匹配第三操作數,並且第三操作數 是否可以轉換以匹配第二操作數。如果兩者都可以被轉換爲 ,或者可以轉換但轉換不明確,則該程序格式不正確。如果兩者都不能轉換,則操作數 保持不變,並且進一步的檢查按照下面描述的 執行。 如果只有一次轉換是可能的,則該轉換爲 應用於所選操作數,轉換後的操作數用於本節其餘部分原始操作數的 位置。

我們瞭解Derived()複製初始化最後Base操作,大家可以看一下§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 2015-04-01 01:25:20

+0

我很困惑,爲什麼移動構造函數在這裏涉及?它試圖移動什麼,它試圖移動到什麼地方? – 2015-04-01 01:27:41

+0

@dyp,這將是我的不好。讓我回去看看。 – chris 2015-04-01 01:28:17