我真的需要两个版本的==运算符重载吗?

时间:2017-03-06 04:53:52

标签: c++ c++11 operator-overloading c++14

我是否必须提供两个不同版本的运算符==重载,以便无论LHS和LHS如何都可以运行。表达式的RHS变体。例如

Class A {

...
bool operator==(int const& L, A const& R);
bool operator==(A const& L, int const& R);
...
};

用于 -

A a;
int x = 8;
if( a == 5 || x == a){
  ...
}

为什么需要这样做?不是L == R == R == L

3 个答案:

答案 0 :(得分:9)

C ++没有将任何运算符定义为可交换或对称。因此,它无法自动翻译:

if( x == a){

if( a == x){

反之亦然。

如果您希望编译器能够正确处理

if( x == a){

您必须将第一个对象的类型operator==重载为intint const&

答案 1 :(得分:2)

如果存在从intA的隐式转换,则可以免费获得对称。

class A
{
    int i_;
public:
    constexpr A(int i) noexcept : i_{i} {}

    friend constexpr bool operator==(A const& L, A const& R) noexcept
        {return L.i_ == R.i_;}
};

int
main()
{
    constexpr A a1{1};
    static_assert(a1 == 1, "");
    static_assert(1 == a1, "");
}

如果没有,或者您不想在比较时支付隐式转化费用,那么您必须在需要时手动提供。

这是一种自洽的语言设计。如果您不希望从intA进行隐式转换(反之亦然),那么您可能不希望能够隐式转换暗示Aint可以相等。但是你可以通过编写自己的异构比较来选择加入这样的设计。

答案 2 :(得分:1)

有一种方法可以做到这一点,而不必每次都写==

namespace bob {
struct equality_support {
  template<class T, class U>
  using supported =
    std::integral_constant< bool,
      std::is_base_of< equality_support, std::decay_t<T> >{}
      && decltype( (void(std::declval<T>().equals( std::declval<U>() ) ), true) ){true}
    >;

  template<class T, class U,
    class=std::enable_if_t< supported<T const&, U const&>{} >
  >
  friend bool operator==( T const& t, U const& u ) {
    return t.equals(u);
  }
  template<class T, class U,
    class=std::enable_if_t<
      !supported<T const&, U const&>{}
      && supported<U const&, T const&>{}
    >,
    class=void
  >
  friend bool operator==( T const& t, U const& u ) {
    return u.equals(t);
  }
  template<class T, class U>
  friend auto operator!=( T const& t, U const& u )
  -> decltype( !(t==u) )
  { return !(t==u); }
};
}

现在,如果我写的那么正确,则从equality_support派生,然后覆盖bool equals( int ) const,它将同时发挥作用。

struct A:
  bob::equality_support
{
  bool equals( int x ) const { return x==42; }
};

测试代码:

int main() {
    A a;
    std::cout << (a==7) << (a!=7) << (a==42) << (a!=42) << '\n';
    std::cout << (a==0.0) << (a=='a') << '\n';
    // std::cout << (a == "hello") << "\n"; // does not compile
}

Live example

我会选择比bob更好的命名空间名称。

如果您想支持A==A,可以执行bool equals( A const& ) const或转换为int

这种技术的运行时间成本接近于零,假设您的编译器进行适度优化和内联。

我所做的是利用参数依赖查找和SFINAE来允许继承equality_support来编写operator==operator!=,最终调度到(非虚拟){{1}继承自equals

的类型的方法

它以对称的方式执行此操作,除非equality_support的两边都有==,在这种情况下更喜欢调用equality_support。因此,如果lhs.equals(rhs)方法是不对称的,那么在这种情况下您将得到不对称的结果。

.equals会自动写入以反转!=

我写的示例==始终比较等于struct A;这大多是个玩笑。它可以在42的主体中做任何想做的事。

我写的SFINAE必须重写才能在MSVC 2015中可靠地工作。它名义上只是一个C ++ 11编译器;它在SFINAE地区的实施仍然存在很大差距。