Why SFINAE has different behavior with gcc <11 vs >12?
Asked Answered
B

2

7

I saw this example of using SFINAE to check if a type is streamable here. However, I noticed that it is not portable, i.e. returns different results for templated types with different compilers. I'd be glad for any tips to understand the problem here.

The code below returns true, false with any version of clang++ and GCC 12 or higher, but true, true with earlier versions of GCC.

You can try it online here.

#include <iostream>
#include <type_traits>
#include <vector>

template <typename T, typename dummy = void>
struct is_printable : std::false_type {};

template <typename T>
struct is_printable<
    T, typename std::enable_if_t<std::is_same_v<
           std::remove_reference_t<decltype(std::cout << std::declval<T>())>,
           std::ostream>>> : std::true_type {};

template <typename T>
inline constexpr bool is_printable_v = is_printable<T>::value;

struct C {};
std::ostream& operator<<(std::ostream& os, const C& c) {
    os << "C";
    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
    for (const auto& el : v) {
        os << el;
    }
    return os;
}

int main(int argc, const char* argv[]) {
    std::cout << std::boolalpha;
    std::cout << is_printable_v<C> << std::endl;
    std::cout << is_printable_v<std::vector<int>> << std::endl;
    return 0;
}
Bialy answered 6/9, 2022 at 5:41 Comment(1)
I don't know if it may help, but if the std::ostream& operator<< is before the struct is_printable both version are "true true"Lurlene
A
6

operator<<(std::ostream& os, const std::vector<T>& v) won't be found by ADL (for std::vector<int>, it would for std::vector<C>) (and so need to be declared before usage to be usable).

That is why correct answer is true, false. previous version of gcc misbehave on this.

Note: It is discouraged to overload operator for types which you don't own. std might in the future add that overload, and you will have ODR (One definition rule) violation. Same if another library does the same wrong thing than you.

Axinomancy answered 6/9, 2022 at 8:3 Comment(2)
Thank you @Jarod42. I realized that adding the overload to std namespace also works, i.e. all compilers agree that vector is printable. However, I see your concern about ODR so I was wondering if adding the overload to the std namespace cause any other problems? If the only concern about ODR is a future overload, then it is ok for me.Bialy
Adding most stuff in std is pedantically forbidden. only some specialization are ok. cleaner solution is to have a wrapper around vector, so you can have operator<<(std::ostream& os, const PrettyPrinter<std::vector<T>>& v).Axinomancy
L
1

Adding a forwarding declaration help:

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v);

Full code:

#include <iostream>
#include <type_traits>
#include <vector>

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v);


template <typename T, typename dummy = void>
struct is_printable : std::false_type {};

template <typename T>
struct is_printable<
    T, typename std::enable_if_t<std::is_same_v<
           std::remove_reference_t<decltype(std::cout << std::declval<T>())>,
           std::ostream>>> : std::true_type {};

template <typename T>
inline constexpr bool is_printable_v = is_printable<T>::value;

struct C {};
std::ostream& operator<<(std::ostream& os, const C& c) {
    os << "C";
    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
    for (const auto& el : v) {
        os << el;
    }
    return os;
}

int main(int argc, const char* argv[]) {
    std::cout << std::boolalpha;
    std::cout << is_printable_v<C> << std::endl;
    std::cout << is_printable_v<std::vector<int>> << std::endl;
    return 0;
}

Demo : https://godbolt.org/z/qKz537TPr

I know I didn't answer "why both version behave differently" but I think it may help :)

Lurlene answered 6/9, 2022 at 5:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.