I'm just playing around with some SFINAE code and I can't get it to work. I'm using Xcode 12.2 with -std=c++17. godbolt reveals that Clang does indeed choose the false_type variant no matter what, whereas gcc uses the true_type variant if both template parameters are identical and the operator is implemented, as I would expect it to. See the code below:
#include <iostream>
#include <type_traits>
struct MyStruct {};
std::ostream& operator<<(std::ostream& os, const MyStruct&) {
return os;
}
template<typename T, typename U = void>
struct has_ostream_operator : std::false_type {};
template<typename T>
struct has_ostream_operator<T, typename std::enable_if_t<sizeof(std::declval<std::ostream&>() << std::declval<T>()), T>> : std::true_type {};
int main() {
constexpr bool res = has_ostream_operator<MyStruct, MyStruct>::value;
if constexpr(res) {
std::cout << "Yes";
} else {
std::cout << "No";
}
return 0;
}
Can someone explain to me what's going on here and why Clang and gcc would behave differently? I've already solved the problem using a different technique, so this is merely for educational purposes :-)
If you replace the whole sizeof
construct with 42
, both compilers give you an error about how sizeof
isn't a bool
and cannot be narrowed to one.
If you replace it with 1
, there is no error (as 1 can obviously be narrowed to true
).
With this in mind, let's pass a bool
explicitly:
template<typename T>
struct has_ostream_operator<
T,
typename std::enable_if_t<
sizeof(std::declval<std::ostream&>() << std::declval<T>()) != 0,
// ^^^^^
T
>
> : std::true_type {};
Now it works.
Both implementations give me 272
for sizeof(std::declval<std::ostream&>() << std::declval<MyStruct>())
, so it's just they're handling this error differently during SFINAEness. I think it's GCC bug 57891.
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