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

1

6

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

Linhliniment answered 7/12, 2020 at 19:32 Comment(1)
Ah, found a dupe. Can delete my answer and dupe-close if you like.Ween
W
4

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.

Ween answered 7/12, 2020 at 19:42 Comment(2)
OP says they've solved the problem. They're asking why their code is treated differently by gcc and clang.Nellynelms
@Nellynelms They switched to a completely different approach. This answer is about what was wrong with this approach, and by my understanding that is what they want to know.Ween

© 2022 - 2024 — McMap. All rights reserved.