如果模板参数也提供比较运算符,则有条件地提供比较运算符的重载

Conditionally providing overload of comparizon operator if template parameter provides it too

我有一个模板 class

template <typename T>
class SometimesComparable
{
public:
  T x1;
  T x2;
  // Other functionally provided unconditionally
  // ...
  // To be provided only if T provides operator<
  // bool operator<(SometimesComparable const & other) const 
  // {
  //   return x1 < other.x1 && x2 < other.x2;
  // }
};

当且仅当其模板参数也提供 bool operator<()

时才应提供 bool operator<()

我读过类似的 questions/answers 使用 SFINAE 但一定有一些我不明白的地方,因为我没有设法使这个想法适应这种情况。

模仿那些答案我有一个 class

template <typename T>
class HasLessThan
{
private:
    typedef char YesType[1];
    typedef char NoType[2];

    template <typename C> static YesType& test( decltype(&C::operator<)     );
    template <typename C> static NoType& test(...);

public:
    enum { value = sizeof(test<T>(0)) == sizeof(YesType) };
};

通过其成员 value 使用其方法 test 来检测 class T 是否提供 operator<.

里面SometimesComparableclass我在定义

typename std::enable_if<HasLessThan<T>::value, bool>::type
operator<(ConditionalMethodProvided &other)
{
    return x1 < other.x1 && x2 < other.x2;
}

然后,为了测试,为了一个有效的用途,我有一个 class

class TypeWithLessThan
{
public:
  int x;
  TypeWithLessThan(int x) : x(x) {};
  bool operator<(TypeWithLessThan &other) {return x < other.x;};
};

int main(int argc, char *argv[])
{
    ConditionalMethodProvided C(TypeWithLessThan(2), TypeWithLessThan(3));
    ConditionalMethodProvided D(TypeWithLessThan(5), TypeWithLessThan(7));
    std::cout << (C < D) << std::endl;

    return 0;
}

问题部分:这样就可以了。现在,我缺少的是如何实现

int main(int argc, char *argv[])
{
    ConditionalMethodProvided C(2, 3);
    ConditionalMethodProvided D(5, 7);
    std::cout << (C < D) << std::endl;

    return 0;
}

也编译成功。

我尝试添加到 SometimesComparable 好友方法

friend
typename std::enable_if<HasLessThan<T>::value, bool>::type
operator<(ConditionalMethodProvided & a1, ConditionalMethodProvided &a2)
{
    return a1.x1 < a2.x1 && a1.x2 < a2.x2;
};

同时拥有第一个 operator< 和友元,会产生模棱两可的重载,没有它会导致 ConditionalMethodProvided<int, int> 的比较无法编译。

我希望 intTypeWithLessThan 都能工作。


编辑:

单块代码

#include <iostream>
#include <type_traits>

template <typename T>
class HasLessThan
{
private:
    typedef char YesType[1];
    typedef char NoType[2];

    template <typename C> static YesType& test( decltype(&C::operator<) );
    template <typename C> static NoType& test(...);

public:
    enum { value = sizeof(test<T>(0)) == sizeof(YesType) };
};

template <typename T>
class ConditionalMethodProvided
{
public:
    T x1;
    T x2;
    ConditionalMethodProvided(T&& a1, T&& a2) : x1(a1), x2(a2) {};

    // This and the next method may not be needed at the same time.
    typename std::enable_if<HasLessThan<T>::value, bool>::type
    operator<(ConditionalMethodProvided &other)
    {
        return x1 < other.x1 && x2 < other.x2;
    };

    template <typename U,
              std::enable_if_t<std::is_same_v<U, T>, bool> = true>
    auto operator< (ConditionalMethodProvided<U> & oth)
        -> decltype( std::declval<U>() < std::declval<U>(), bool{} )
        { return x1 < oth.x1 && x2 < oth.x2; }
};

class TypeWithLessThan
{
public:
  int x;
  TypeWithLessThan(int x) : x(x) {};
  bool operator<(TypeWithLessThan &other) {return x < other.x;};
};

int main(int argc, char *argv[])
{
    // The question is how to to make the next two types, int and TypewithLessThan both make the templated class ConditionalMethodProvided to provide the operator< method.
    ConditionalMethodProvided C(TypeWithLessThan(2),     TypeWithLessThan(3));
    ConditionalMethodProvided D(TypeWithLessThan(5),     TypeWithLessThan(7));
    std::cout << (C < D) << std::endl;

    ConditionalMethodProvided E(2,3);
    ConditionalMethodProvided F(5,7);
    std::cout << (E < F) << std::endl;

    return 0;
}

下面呢?

template <typename T>
class SometimesComparable
 {
   public:
      T x1;
      T x2;

      template <typename U, 
                std::enable_if_t<std::is_same_v<U, T>, bool> = true>
      auto operator< (SometimesComparable<U> const & oth)
         -> decltype( x1 < oth.x1, bool{} )
       { return x1 < oth.x1 && x2 < oth.x2; }
 };

我的意思是...如果你想要 SFINAE enable/disable 一个方法,你必须把这个方法做成一个模板,所以

  template <typename U>
  bool operator< (SometimesComparable<U> const & oth)
   { /* something */ }

但我想你希望 UT 是同一类型,所以你可以通过 std::enable_if_t

强加这个
  template <typename U, 
            std::enable_if_t<std::is_same_v<U, T>, bool> = true>
  bool operator< (SometimesComparable<U> const & oth)
   { /* something */ }

现在你必须启用 SFINAE 运算符 iff(当且仅当)你可以写 x1 < oth.x1(当为 U 定义运算符时)所以,使用 auto,尾随 return 类型和 decltype(),你可以写

  auto operator< (SometimesComparable<U> const & oth)
     -> decltype( x1 < oth.x1, bool{} )
   { /* something */ }

或者如果您确定 x1 < oth.x1 给出 bool 值,也可以简单地 decltype( x1 < oth.x1 )

为了以后参考,你可以用C++20的概念写这个:

    // To be provided only if T provides operator<
    auto operator<(SometimesComparable const & other) const -> bool
        requires requires(T a, T b) {
            {a < b} -> bool;
        }
    {
        return x1 < other.x1 && x2 < other.x2;
    }