【问题标题】:Conditionally providing overload of comparizon operator if template parameter provides it too如果模板参数也提供了比较运算符,则有条件地提供重载
【发布时间】:2019-09-10 12:24:17
【问题描述】:

我有一个模板类

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&lt;() 当且仅当它的模板参数也提供bool operator&lt;()

我已经阅读了使用 SFINAE 的类似问题/答案,但肯定有一些我不明白的地方,因为我没有设法使这个想法适应这种情况。

模仿那些答案我有一堂课

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 检测类T 是否提供operator&lt;

在我定义的 SometimesComparable 类中

typename std::enable_if<HasLessThan<T>::value, bool>::type
operator<(ConditionalMethodProvided &other)
{
    return x1 < other.x1 && x2 < other.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[])
{
    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&lt; 和朋友的 operator&lt; 会创建一个模棱两可的重载,没有它会导致 ConditionalMethodProvided&lt;int, int&gt; 的比较无法编译。

我希望 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;
}

【问题讨论】:

    标签: c++ templates c++17 sfinae


    【解决方案1】:

    下面呢?

    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 启用/禁用一个方法,你必须让这个方法成为一个模板,所以

      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 运算符,当且仅当您可以编写 x1 &lt; oth.x1(当为 U 定义运算符时)所以,使用 auto,尾随返回类型和 decltype(),你可以写

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

    如果您确定x1 &lt; oth.x1 给出了bool 值,也可以简单地使用decltype( x1 &lt; oth.x1 )

    【讨论】:

    • 这听起来很合理,但是当我使用它(作为类中的唯一方法)时,C &lt; DCD 类型为 SometimesComparable&lt;TypeWithLessThan&gt; 的情况下,编译器说没有版本operator&lt; 匹配。当用于ints 时,它确实有效。但我不能同时添加两个版本(对我和你都有效的那个),因为我的错误是 int
    • @conditionalMethod - 对不起,但对我来说并不完全清楚。请问,你能在你的问题中添加一个最小但完整的例子吗?
    • 会的。已经发布的代码是完整的,尽管由于页面要求有更多的单词,所以中间有单词。让我将它们全部收集在一个代码块中。
    • 已添加。它包含我所拥有的方法的版本和您的建议。如果你注释掉我的版本和 ConditionalMethodProvided&lt;TypeWithLessThan, TypeWithLessThan&gt; 的主要实例,它会编译。相反,如果您在它编译的主文件中注释掉 ConditionalMethodProvided&lt;int, int&gt; 的实例。但是这两种类型的使用是我没有成功的。
    • @conditionalMethod - 不清楚你到底想要什么,反正......你的版本不能工作,因为不是模板运算符;如果你想让 SFINAE 工作,你必须写如下内容:template &lt;typename U = T&gt; std::enable_if_t&lt;HasLessThan_v&lt;U&gt;, bool&gt; operator&lt;(ConditionalMethodProvided &amp;other) { return x1 &lt; other.x1 &amp;&amp; x2 &lt; other.x2; }。注意HasLessThan_v检查U,方法的模板类型,而不是T
    【解决方案2】:

    为了将来参考,你可以用 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;
        }
    

    【讨论】:

    • @conditionalMethod 很快,在 C++20 中。 clang 中有实验性实现:godbolt.org/z/e-TDk4 和 gcc 也有使用 -fconcepts 实现的概念,但是 afaik 这是一个旧提案,而不是标准草案。
    猜你喜欢
    • 2019-06-14
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 2019-02-24
    相关资源
    最近更新 更多