【问题标题】:Return value of operator++ [duplicate]运算符++的返回值[重复]
【发布时间】:2013-07-31 01:33:28
【问题描述】:

我的以下代码已损坏。我可以通过修改代码中的某些行来修复它(参见注释)。问题的原因是什么?

#include <iostream>
using namespace std;

class Number{
public:
    int n;
    Number(int a):n(a){}

    //when I change the following to
    //friend Number& operator++(Number& source, int i)
    //then it compiles fine and correct value is printed
    friend Number operator++(Number& source, int i){
        ++source.n;
        return source;
    }
};

int main() {

    Number x(5);
    x++++; //error: no 'operator++(int)' declared for postfix '++' [-fpermissive]
    cout<<x.n;

    return 0;
}

【问题讨论】:

  • 为什么这会让您感到惊讶?想一想后缀 operator++ 的语义,你要返回什么,你想做什么以及你想对谁做。
  • 为什么你的代码中需要friend
  • @triclosan 这里没有必要,但是假设他有一个私有成员并且想要一个全局函数而不是成员重载?
  • @triclosan 因为 operator++ 是一元的,如果它是一个成员函数,它将只有一个参数(int)。我相信这个有两个参数的版本不是成员函数(即使它是在类中定义的),因此需要这个朋友。

标签: c++


【解决方案1】:

您尝试将第二个++ 应用于第一次调用返回的临时对象。但是,操作数必须通过引用传递,并且不能将临时值绑定到非常量 lvalue 引用。

您可能不想“修复”这个问题,因为几乎没有理由修改这样的临时值。但是,您应该在递增它之前返回值的副本,以提供预期的递增后行为。

前缀运算符应该返回一个引用,它可以愉快地绑定到另一个引用,以便++++x; 应该按预期工作。

【讨论】:

  • 问题是后缀增量不能通过引用返回,因为它会返回已经增加的值!
  • @MarkB:所以它不能;我没有正确阅读代码。
  • 他为什么要把operator++ 变成friend
  • @NikBougalis:因为,当您不编写玩具类作为演示时,您可能希望数据成员是私有的。虽然它可以是会员,但如果您愿意的话。
  • @NikBougalis:让它成为非会员的一个很好的理由是,如果你写x++++;,你会得到一个错误而不是意外的结果,正如问题所证明的那样。
【解决方案2】:

您正在通过编写x++ ++ 来增加内部operator++ 的返回值。这意味着如果该运算符的返回值不是可以修改的,则代码将无法编译。

所以如果你声明它返回Number而不是Number &amp;,那么它就不能被修改(函数的返回值是临时的而不是左值,除非它是一个引用,因此外部运算符++,它通过(非常量)引用获取它,不能将它绑定到由值返回的对象)。

【讨论】:

  • 您可以很好地修改类类型的临时/右值。问题是,如果按值返回,则无法链接运算符,因为运算符通过非常量引用获取第一个参数。
  • @jrok(我实际上怀疑有类似的事情,但我不确定。)所以,如果我添加“因此外部operator++”,这将是正确的(非const ) 引用,不能将它绑定到由值返回的对象”?
  • 没问题,我想。
  • @jrok 感谢您的建议!
  • 现在,为恐怖做准备:ref-qualified member functions work in clang!(当然没有用例,但仍然:/) PS:你收到我的invite了吗?
【解决方案3】:

您正在尝试做的事情非常不寻常。后增量通常返回一个代表对象增量之前的右值(与前增量相反,它首先增加对象,然后返回该对象本身,作为左值)。出于莫名其妙的原因,您基本上是在尝试使后增量的行为与前增量相同。

通常,您会这样做:

class Number {
  int n;
public:
  // Pre-increment
  Number& operator++() {
    ++n;
    return *this;
  }
  Number operator++(int) {
    Number temp = *this;  // capture old value
    ++(*this);
    return temp;
  }
};

有了这个定义,x++++ 不会编译 - 但是当 xint 时它也不会编译:它并没有多大意义。

无论如何,它对您不起作用的原因如下。 x++++ 被解释为

operator++(operator++(x, 0), 0)

内部的operator++ 调用返回一个临时的Number 对象。外部operator++() 需要Number&amp; 类型的参数 - 但非常量引用不能绑定到临时。当您更改声明以使 operator++ 返回 Number&amp; - 一个左值时 - 然后此返回值可以愉快地传递给外部 operator++ 调用。

【讨论】:

  • 我认为给定您的代码,x++++ 实际上会编译,因为x.operator++(0).operator++(0) 格式正确。您可以通过声明成员 Number operator++(int) &amp; 来解决此问题(但只有最近的 gcc/clang 版本支持,并且 AFAIK 没有 Microsoft 编译器支持)。
  • @Igor Tandetnik aschepler 是对的 - 它确实可以编译。无论如何,我明白了,谢谢。
【解决方案4】:

让我们首先观察您不能像这样为int 链接后增量运算符!

那么在我解决问题之前,让我建议不要编写这样不直观的代码。一年后有人会读你的程序,而你想让它尽可能容易理解。

考虑x++++ 确实类似于operator++(operator++(x, int), int) 所以现在发生的是第一个operator++ 按值返回(这会导致返回一个未命名的临时值)。这个未命名的临时不能绑定到第二个(外部)调用的非常量引用参数,并且方法查找失败。

最后请注意,您的实现实际上并没有实现后缀增量:它实现了前缀增量。您应该删除 int 参数(表示后缀)或修复实现以返回未修改的值。

【讨论】:

    猜你喜欢
    • 2011-05-27
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 2013-11-08
    • 2012-04-26
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多