This piece of code is dumb of course, but I only wrote it to illustrate the issue. Here it is:
#include <iostream>
using namespace std;
struct foo {
int a = 42;
template <typename T>
operator T* () {
cout << "operator T*()\n";
return reinterpret_cast<T*>(&a);
}
template <typename T>
operator const T* () const {
cout << "operator const T*() const\n";
return reinterpret_cast<const T*>(&a);
}
template <typename T>
T get() {
cout << "T get()\n";
return this->operator T();
}
};
int main() {
foo myFoo;
cout << *myFoo.get<const int*>() << '\n';
}
The output when compiled with Visual Studio 2019 (ISO C++17, /Ox
) is:
T get()
operator const T*() const
42
The output with gcc 8.3 (-std=c++17
, -O3
) is:
T get()
operator T*()
42
So I'm wondering why the two compilers opt to call different const-qualified conversions given this code?
If I change get()
to get() const
, then both call the const
version of the conversion. But isn't VS violating the standard by calling the const
conversion from a method that isn't marked const
?
EDIT:
To clear up some confusion around reinterpret_cast
, here's a version without it which still produces the same output on both compilers.
The method:
template <typename T> foo::T get();
is not const
.
That implies inside its body the object this
is a pointer to foo
type (and not const foo
).
Therefore, the statement
this->operator T();
is going to call the no-const
version because of the overload resolution.
As the standard states on [over.match.best]
, the version no-const
is preferred because does not require any cast.
Indeed, in order to call the const
version, the compiler should have implicitly cast into a const object (i.e. const_cast<const foo*>(this)
).
Both gcc and clang follow what I have just said.
MSVC simply does not follow the standard here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With