2017-02-27 19 views
3

運行此代碼時,Clang(3.9.1)和GCC(7,快照)將「1」,「2」打印到控制檯。MSVC是否有權找到此方法調用模糊,而Clang/GCC不?

但是,MSVC無法編譯此代碼:

source_file.cpp(15): error C2668: 'Dictionary::set': ambiguous call to overloaded function

source_file.cpp(9): note: could be 'void Dictionary::set(int64_t)'

source_file.cpp(8): note: or 'void Dictionary::set(const char *)'

source_file.cpp(15): note: while trying to match the argument list '(const unsigned int)'

#include <iostream> 

static const unsigned ProtocolMajorVersion = 1; 
static const unsigned ProtocolMinorVersion = 0; 

class Dictionary { 
public: 
    void set(const char *Str) { std::cout << "1"; } 
    void set(int64_t val) { std::cout << "2"; } 
}; 

int main() { 
    Dictionary dict; 
    dict.set(ProtocolMajorVersion); 
    dict.set(ProtocolMinorVersion); 
} 

我認爲MSVC是正確的 - 的ProtocolMajorVersion0,它可以是NULLint64_t(0)

然而,這似乎是這樣與

dict.set(0); 

source_file.cpp:15:10: error: call to member function 'set' is ambiguous dict.set(0);

source_file.cpp:8:10: note: candidate function

void set(const char *Str) { std::cout << "1"; } 

source_file.cpp:9:10: note: candidate function

void set(int64_t val) { std::cout << "2"; } 

那麼,什麼是怎麼回事更換

​​

時 - 這編譯器是正確的?如果GCC和Clang都接受不正確的代碼,或者MSVC僅僅是越野車,會令我感到驚訝嗎?請參考標準

+2

它是不明確的,因爲類型不匹配,所以它可能是。 'unsigned'的大小不一定是64位,所以它不太可能匹配64_t類型。就我個人而言,我更喜歡MSVC的方式 - 在C/C++程序中注意含糊不清總是件好事。 –

回答

4

在C++ 11和前,任何積分常量表達式其計算結果爲0是被認爲是空指針恆定。這在C++ 14中受到限制:只考慮值爲0的整數文字。另外,從C++ 11開始,std::nullptr_t類型的prvalues是空指針常量。請參閱[conv.ptr]和CWG 903

關於過載的分辨率,二者的積分轉換unsigned - >int64_t和指針轉換空指針恆定 - >const char*具有相同的等級:轉換。參見[over.ics.scs] /表12.

所以如果ProtocolMinorVersion被認爲是空指針常量,那麼調用是不明確的。如果您只編譯以下程序:

static const unsigned ProtocolMinorVersion = 0; 

int main() { 
    const char* p = ProtocolMinorVersion; 
} 

您將看到clang和gcc拒絕此轉換,而MSVC接受它。

由於CWG 903被認爲是一個缺陷,我認爲clang和gcc是正確的。

+0

儘可能有趣,我想給'struct S {int x; },'S()。x'是一個空指針常量,有點太多了:)這部分C++ 11沒有CWG 903是沒有意義的。 –

1

當兩個編譯器同意而另一個不同意時,它幾乎總是那個沒有錯的。

我會爭辯說,如果你聲明一個值爲const unsigned somename = 0;,它不再是一個簡單的零,它是一個名爲無符號常量,其值爲零。所以不應該被視爲等同於指針類型,只留下一個似是而非的候選者。

話雖如此,兩者的set功能需要轉換(它不是一個uint64_t,既不是const char *),所以人們可以說,MSVC是正確的[編譯器應挑選需要至少轉換的類型,如果有多個類型要求等價的轉換,這是模棱兩可的] - 雖然我仍然不認爲編譯器應該接受一個值爲0的命名常量作爲一個指針的等價物...

對不起,可能更多的是「評論」比一個答案 - 我開始寫作時打算說「gcc/clang是對的」,但後來更多地思考這個結論,得出結論「雖然我會對這種行爲感到高興,但不清楚這是否是正確的行爲」 。

+2

積分轉換(如'int' - >'long long')和指針轉換(如'0' - >'char *')具有相同的等級:轉換。在C++ 11中,可以使用任何常量整型表達式(它是*空指針常量*)來初始化指針;這已被限制在C++ 14中,請參閱http://wg21.link/cwg903 – dyp

+0

@dyp感覺像將該評論轉換爲答案? –

+1

不同意第一段,這是一個相當薄弱的啓發式IMO –

相關問題