2013-07-30 94 views
11

我有下面的代码被破坏。我可以通过修改代码中的某一行来修复它(请参阅评论)。问题的原因是什么?运算符的返回值++

#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; 
} 
+1

这为什么让你感到惊讶?想想postfix'operator ++'的语义,你回来了什么,你正在尝试做什么以及你想要为谁做什么。 –

+0

为什么你的代码中需要'friend'? – triclosan

+0

@triclosan在这里没有必要,但假设他有一个私人成员,并且想要一个全局函数而不是成员超载? –

回答

16

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

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

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

+0

问题是,后缀增量*不能*通过引用返回,因为它会返回已经递增值! –

+0

@MarkB:所以它不能;我没有正确阅读代码。 –

+0

为什么他让'operator ++'成为'friend'开头? –

8

你被写x++ ++递增内operator++的返回值。这意味着如果该运算符的返回值不是可以修改的,则代码将不会编译。

所以如果声明它返回Number代替Number &,那么它不能被修改(一个函数的返回值是一个暂时的,不是左值,除非它是一个参考,因此外操作者++,这需要它通过(非const)引用,无法将其绑定到由值返回的对象)。

+0

你可以很好地修改类类型的临时值/右值。问题在于,如果按值返回,则不能链接运算符,因为运算符通过非const引用接受第一个参数。 – jrok

+0

@jrok(我真的怀疑过类似的东西,但我不确定。)所以,如果我添加了“因此外部'运算符++',它通过(非const)引用,无法绑定它到一个对象返回值“? – 2013-07-30 20:03:33

+0

没关系,我想。 – jrok

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&类型的参数 - 但非const引用不能绑定到临时。当您更改声明以便operator++返回Number& - 一个左值 - 则此返回值可以愉快地传递给外部operator++调用。

+0

我想给你的代码,'x ++++'实际上会编译,因为'x.operator ++(0).operator ++(0)'是格式良好的。你可以通过声明成员'Number operator ++(int)&'(但只有最近的gcc/clang版本支持这个,而AFAIK没有微软的编译器)来解决这个问题。 – aschepler

+0

@Igor Tandetnik aschepler是正确的 - 它编译。无论如何,我明白了,谢谢。 – Slazer

0

让我们开始观察,你不能连锁这样的后增量运算符为int

然后,在我得到这个问题之前,让我建议不要写这样不直观的代码。有人会从现在开始阅读你的程序,并且希望尽可能地让它更容易理解。

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

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