Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE code to detect whether operator<< is implemented for a type behaves differently on Clang and gcc [duplicate]

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 :-)

like image 515
Matthias Grün Avatar asked Dec 07 '20 19:12

Matthias Grün


1 Answers

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.

like image 109
Asteroids With Wings Avatar answered Oct 24 '22 13:10

Asteroids With Wings