EqualityComparable 特质解释

EqualityComparable trait explanation

阅读第二版 C++ 模板的第 22 章,我试图理解 EqualityComparable 特性的实现。但是我不明白编译器是如何决定是否激活回退的。

除此之外还有两个只声明但程序编译运行的函数。这对我来说很奇怪。

这是代码。 头文件IsEqualityComparable.hpp

#include <utility>        // for declval()
#include <type_traits>    // for true_type and false_type

template<typename T>
class IsEqualityComparable
{
 private:
 // test convertibility of == and ! == to bool:
 static void* conv(bool);  // to check convertibility to bool
 template<typename U>
 static std::true_type test(decltype(conv(std::declval<U const&>() ==
                                        std::declval<U const&>())),
                          decltype(conv(!(std::declval<U const&>() ==
                                          std::declval<U const&>())))
                         );
// fallback:
template<typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(nullptr,
                                             nullptr))::value;
};

源文件如下

#include <iostream>
#include <exception>
#include <utility>
#include <functional>
#include "isequalitycomparable.hpp"

template<typename T,
     bool EqComparable = IsEqualityComparable<T>::value>
struct TryEquals
{
  static bool equals(T const& x1, T const& x2) {
  std:: cout << "IsEqualityComparable equals::"<<std::endl;
  return x1 == x2;
}
};
class NotEqualityComparable : public std::exception
{
};
template<typename T>
struct TryEquals<T, false>
{
  static bool equals(T const& x1, T const& x2) {
  std:: cout << "Throw::"<<std::endl;
  throw NotEqualityComparable();
}
};
void foo(int)
{
} 
void bar(int)
{
}
class A
{
 public:
 A() = default;
 friend bool operator ==(A a1 , A a2)
 {
    return true;
 }
};
int main()
{
 std:: cout << "Enter" << std::endl;
 std::function<void(int)> f = foo;
 std::function<void(int)> f2 = f;
 std:: cout << "Enter" << std::endl;
 //std:: cout << "Check::"<< 
 //TryEquals<std::function<void(int)>>::equals(f,f2) << std::endl;
 A a1;
 A a2;
 std:: cout << "Check::"<< TryEquals<A>::equals(a1,a2) << std::endl;
 return 0;
}

TryEquals<std::function<void(int)>>::equals(f,f2)

抛出异常,因为运算符 == 未实现但

TryEquals<A>::equals(a1,a2)  

returns 1 因为 class A 有一个运算符 ==。

在这一点上,我需要帮助来理解 conv 和 test 是如何工作的。

此外

static constexpr bool value = decltype(test<T>(nullptr,
                                               nullptr))::value

有效吗?

我对这个表达感到困惑

decltype(test<T>(nullptr,nullptr))::value. 

不需要定义这些函数,因为它们实际上从未被调用过。

decltype,它计算出函数的 return type,但从不尝试计算 return .

在这种情况下它与sfinae组合在一起,这样如果decltype无法找出==的return类型(可能是因为运算符没有存在)test 的重载将被忽略。然后 test(...) 将被选中。

这利用了 ... 是参数类型的绝对最差匹配这一事实,因此只有在没有其他重载可用时才会使用它(因此 "fallback")。

顺便说一下,std::declval 也从未定义过。