【问题标题】:Overloading ++ for both pre and post increment为前后增量重载 ++
【发布时间】:2013-02-21 01:14:47
【问题描述】:

我们可以重载operator++ 用于预增量和后增量吗?即调用 SampleObject++++SampleObject 会得到正确的结果。

class CSample {
 public:
   int m_iValue;     // just to directly fetch inside main()
   CSample() : m_iValue(0) {}
   CSample(int val) : m_iValue(val) {}
   // Overloading ++ for Pre-Increment
   int /*CSample& */ operator++() { // can also adopt to return CSample&
      ++(*this).m_iValue;
      return m_iValue; /*(*this); */
   }

  // Overloading ++ for Post-Increment
 /* int operator++() {
        CSample temp = *this;
        ++(*this).m_iValue;
        return temp.m_iValue; /* temp; */
    } */
};

我们不能只根据返回类型重载一个函数,而且即使我们认为它是允许的,由于重载决策的模糊性,它也不能解决问题。

既然提供了运算符重载来使内置类型表现得像用户定义的类型,为什么我们不能同时为我们自己的类型使用前置和后置增量?

【问题讨论】:

  • 就个人而言,我从不实现或编写后增量。 IMO 使用它会导致代码非常复杂。
  • @LCIDFire 如果没有它,实现迭代器将非常棘手。
  • 使用 C++20,你会得到 default comparisons(又名“宇宙飞船操作员”)。

标签: c++ operator-overloading


【解决方案1】:

[N4687]

16.5.7

名为 operator++ 的用户定义函数实现了前缀和后缀 ++ 运算符。如果这个函数是一个没有参数的非静态成员函数,或者一个有一个参数的非成员函数,它为该类型的对象定义了前缀自增运算符++。如果函数是一个有一个参数的非静态成员函数(应该是 int 类型)或一个有两个参数的非成员函数(第二个应该是 int 类型),它定义了后缀自增运算符 ++对于该类型的对象。当使用 ++ 运算符调用后缀增量时,int 参数的值为零

示例:

struct X {
  X&   operator++();    // prefix ++a
  X    operator++(int); // postfix a++
};

struct Y { };

Y&   operator++(Y&);      // prefix ++b
Y    operator++(Y&, int); // postfix b++

void f(X a, Y b) {
  ++a; // a.operator++();
  a++; // a.operator++(0);
  ++b; // operator++(b);
  b++; // operator++(b, 0);

  a.operator++();     // explicit call: like ++a;
  a.operator++(0);    // explicit call: like a++;
  operator++(b);      // explicit call: like   ++b;
  operator++(b, 0);   // explicit call: like b++;
}

【讨论】:

【解决方案2】:

类型 T 的前置增量和后置增量的标准模式

T& T::operator++() // pre-increment, return *this by reference
{
 // perform operation


 return *this;
}

T T::operator++(int) // post-increment, return unmodified copy by value
{
     T copy(*this);
     ++(*this); // or operator++();
     return copy;
}

(你也可以调用一个通用函数来执行增量,或者如果它是一个简单的单行函数,比如成员上的 ++,只需在两者中都执行)

【讨论】:

    【解决方案3】:

    增量运算符的后缀版本采用虚拟int 参数以消除歧义:

    // prefix
    CSample& operator++()
    {
      // implement increment logic on this instance, return reference to it.
      return *this;
    }
    
    // postfix
    CSample operator++(int)
    {
      CSample tmp(*this);
      operator++(); // prefix-increment this instance
      return tmp;   // return value before increment
    }
    

    【讨论】:

    • 但是编译器如何进行这种映射,因为我们在使用 ++ 时不传递任何参数 (int)。
    • @ajay 它是用语言定义的。当您调用i++ 时,它将选择版本operator++(int)。这有点像 hack,这就是它的完成方式。
    • 当重载一个函数(即有两个同名的不同函数)时,编译器不能仅仅根据返回类型来区分它们,区分它们的是函数的数量和类型传给他们的论据。这就是为什么后缀增量“人为”添加了一个 int 参数的原因。
    • 在一个普通函数中,CSample的析构函数会在函数返回后被调用。后运算符重载不会发生这种情况吗?
    • @paarandika 从函数返回的值的副本可能会被省略,在这种情况下,不会有复制构造函数或析构函数调用。但是如果副本没有被省略,那么正式地会有一个析构函数调用(模可简单破坏的类型和as-if规则。)
    【解决方案4】:

    为什么我们不能同时为我们自己的类型使用前后增量。

    你可以:

    class CSample {
    public:
    
         int m_iValue;
         CSample() : m_iValue(0) {}
         CSample(int val) : m_iValue(val) {}
    
         // Overloading ++ for Pre-Increment
         int /*CSample& */ operator++() {
            ++m_iValue;
            return m_iValue;
         }
    
        // Overloading ++ for Post-Increment
        int operator++(int) {
              int value = m_iValue;
              ++m_iValue;
              return value;
          }
      };
    
      #include <iostream>
    
      int main()
      {
          CSample s;
          int i = ++s;
          std::cout << i << std::endl; // Prints 1
          int j = s++;
          std::cout << j << std::endl; // Prints 1
      }
    

    【讨论】:

      猜你喜欢
      • 2014-12-09
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      • 2011-09-18
      • 2012-09-15
      • 2012-10-03
      • 2016-07-08
      相关资源
      最近更新 更多