为什么SFINAE在gcc〈11和>12时有不同的行为?

dl5txlt9  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(141)

我看过这个使用SFINAE来检查一个类型是否是可流的here的例子。但是,我注意到它是不可移植的,也就是说,对于不同的编译器,模板化的类型返回不同的结果。我很乐意提供任何提示来理解这里的问题。
下面的代码对于任何版本的clang++和GCC 12或更高版本都返回true, false,但对于早期版本的GCC则返回true, true

您可以在线试用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;
}
5ssjco0h

5ssjco0h1#

operator<<(std::ostream& os, const std::vector<T>& v)不会被ADL找到(对于std::vector<int>,它会被std::vector<C>找到)(因此需要在使用前声明才能使用)。
这就是为什么正确答案是truefalse。以前版本的gcc在这方面表现不佳。
注意:不鼓励为您不拥有的类型重载运算符。std可能在将来添加该重载,并且您将违反ODR(一个定义规则)。如果另一个库做了与您相同的错误事情,也是如此。

x8diyxa7

x8diyxa72#

添加转发声明帮助:

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

完整代码:

#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;
}

演示:https://godbolt.org/z/qKz537TPr
我知道我没有回答“为什么两个版本的行为不同”,但我认为这可能会有帮助:)

相关问题