命名空间中运算符重载解析的规则

时间:2017-11-27 01:00:53

标签: c++11 templates gcc clang metaprogramming

在实现检测存在相等运算符时,我注意到由于某些原因,下面的代码无法在GCC v4,...,v7上编译,但似乎在Clang中编译良好。确切的错误(下面)是由于GCC无法找到名称空间中模板化的重载运算符:

stackoverflow-operator.cpp:15:51: error: no match for 'operator==' (operand types are 'A' and 'A')
std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value  and
stackoverflow-operator.cpp:16:51: error: no match for 'operator!='   (operand types are 'A' and 'A')
std::is_same<bool, decltype(std::declval<T>() != std::declval<T>  ())>::value

有趣的是,在函数foo中使用相同的运算符在GCC和Clang中都很好。

我的问题是:哪个编译器就在这里?如果那是GCC那么为什么在这样的情况下找不到运算符重载是正确的呢?

#include <type_traits>
#include <iostream>

struct A {};


namespace has_equal_operator_impl {

struct NoEqualOperator {};
template<typename T1, typename T2> NoEqualOperator operator== (T1, T2) { return NoEqualOperator(); }
template<typename T1, typename T2> NoEqualOperator operator!= (T1, T2) { return NoEqualOperator(); }

template<typename T>
typename std::enable_if<
             std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value  and
             std::is_same<bool, decltype(std::declval<T>() != std::declval<T>())>::value
             , bool>::type
constexpr has_equal_operator()
{
    return true;
}

template<typename T>
typename std::enable_if<
    not (std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>::value  and
         std::is_same<bool, decltype(std::declval<T>() != std::declval<T>())>::value )
    , bool>::type
constexpr has_equal_operator()
{
    return false;
}

void foo()
{
    auto r = A() == A(); // somehow this works just fine in GCC
}

}

int main()
{
    // Only work in Clang and does not work in GCC because local namespace operators is not consider
    std::cout << "has_equal_operator<int>() --> " << has_equal_operator_impl::has_equal_operator<int>() << std::endl;
    std::cout << "has_equal_operator<A>() --> "   << has_equal_operator_impl::has_equal_operator<A>()   << std::endl;
    return 0;
}

0 个答案:

没有答案