Reputation: 22715
Clang (3.9.1) and GCC (7, snapshot) print "1", "2" to the console when this code is run.
However, MSVC fails to compile this code:
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);
}
I think MSVC is right - the value of ProtocolMajorVersion
is 0
, which can be NULL
or int64_t(0)
.
However, this seems to be the case when replacing
dict.set(ProtocolMinorVersion)
with
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"; }
So what's going on here - which compiler is right? Would surprise me if both GCC and Clang are accepting incorrect code, or is MSVC just being buggy? Please refer to the standard
Upvotes: 3
Views: 587
Reputation: 39141
In C++11 and before, any integral constant expression which evaluates to 0 is a considered a null pointer constant. This has been restricted in C++14: only integer literals with value 0 are considered. In addition, prvalues of type std::nullptr_t
are null pointer constants since C++11. See [conv.ptr] and CWG 903.
Regarding overload resolution, both the integral conversion unsigned
-> int64_t
and the pointer conversion null pointer constant -> const char*
have the same rank: Conversion. See [over.ics.scs] / Table 12.
So if ProtocolMinorVersion
is considered a null pointer constant, then the calls are ambiguous. If you just compile the following program:
static const unsigned ProtocolMinorVersion = 0;
int main() {
const char* p = ProtocolMinorVersion;
}
You will see that clang and gcc reject this conversion, whereas MSVC accepts it.
Since CWG 903 is considered a defect, I'd argue that clang and gcc are right.
Upvotes: 6
Reputation: 129494
When two compilers agree and one doesn't, it's nearly always the one that doesn't that is wrong.
I would argue that if you declare a value as const unsigned somename = 0;
, it is no longer a simple zero, it is a named unsigned constant with the value zero. So should not be considered equivalent to a pointer type, leaving only one plausible candidate.
Having said that, BOTH of the set
functions require conversion (it's not a uint64_t
, neither a const char *
), so one could argue that MSVC is right [the compiler shall pick the type that requires least conversion, if multiple types require equal amount of conversion, it's ambiguous] - although I still don't think the compiler should accept a named constant of the value zero as an equivalent to a pointer...
Sorry, probably more of a "comment" than an answer - I started writing with the intention of saying "gcc/clang are right", but then thinking more about it came to the conclusion that "although I would be happier with that behaviour, it's not clear that this is the CORRECT behaviour".
Upvotes: 1