2014-03-29 134 views
0

我在下面的代码中有冲突。返回一个对象的const引用

#include <iostream> 
using std::cout; 
using std::endl; 

class TestApp { 
public: 

    TestApp(int _x = 9) { 
     cout << "default constructor\n"; 
    } 

    TestApp(const TestApp &app) { 
     cout << "Copy constructor\n"; 
    } 

    ~TestApp() { 
     cout << "destructor\n"; 
    } 

    void setX(int _x) { 
    } 

    const TestApp &operator=(TestApp &obj) { 
     cout << "= operator\n"; 
     return *this; 
    } 

    void setX(int _x) { 
     cout << "Inside setX\n"; 
    } 
}; 

int main() { 
    TestApp app1; 
    TestApp app2; 
    TestApp app3; 
    (app1 = app2) = app3; // 1 
    app1 = app2 = app3; // 2 
    app1.setX(3) 
    return 0; 
} 

我得到这个错误:第1行main.cpp:38: error: passing ‘const TestApp’ as ‘this’ argument of ‘const TestApp& TestApp::operator=(TestApp&)’ discards qualifiers 但是我可以用app1.setX(3);

main.cpp:38: error: no match for ‘operator=’ in ‘app1 = app2.TestApp::operator=(((TestApp&)(& app3)))’ 
main.cpp:28: note: candidates are: const TestApp& TestApp::operator=(TestApp&) 

,并使其工作,我应该让operator=像:

TestApp &operator=(const TestApp &obj) { 
     cout << "= operator\n"; 
     return *this; 
} // works for 1 

TestApp &operator=(TestApp &obj) { 
     cout << "= operator\n"; 
     return *this; 
} // works for 2 

为什么如果我删除const关键字它的作品?并且行1 app1对象不是常量。

+0

而解决方案1不会为第2行工作? – deviantfan

+0

是的,我知道,所以我把评论,以显示代码将工作的哪一行。 –

+0

那么问题是什么? – juanchopanza

回答

2

您不能分配常量对象。例如,考虑这个简单的代码

const int x = 10; 
x = 20; 

,编译器会发出错误的第二次发言,因为x是一个常量对象,并不得转让。

同样是有效的for语句

(app1 = app2) = app3; 

这里表达(app1 = app2)返回恒定参考其可以不被分配,

恒定的参考并不意味着对象本身,它指的是是恒定的。考虑以下示例

int x = 10; 
const int &rx = x; 

x = 20; 
rx = 30; 

虽然rx被定义为常量引用,但您可以更改对象x本身。您可能不会使用该引用来分配对象x,因此编译器将为最后一条语句发出错误。

我们经常在函数的参数声明中使用常量引用,以防止它们在函数内引用的对象发生变化。例如,

void f(const int &x) 
{ 
    x = 20; // compilation error 
} 

int x = 10; 

f(x); 

因此,定义对一个非常量对象的常量引用并不会使该对象本身保持不变。它只能防止使用此引用更改对象。

而且你需要定义只有一个拷贝赋值运算符

TestApp &operator=(const TestApp &obj) { 
     cout << "= operator\n"; 
     return *this; 
} // works for 1 

没有任何需要定义拷贝赋值运算符,

TestApp &operator=(TestApp &obj) { 
     cout << "= operator\n"; 
     return *this; 
} // works for 2 

,如果你不打算更改右边的操作数。所以最好把它定义为一个常量基准const TestApp &obj

当然,你可能会把这两个操作符放在一起,但是没有任何意义可以拥有第二个操作符。

另一方面,你可能没有只有第二个操作符。在这种情况下,您将无法使用将其分配给其他对象的常量对象。

2

正确的方法提供了一个赋值运算符是如下声明它:

TestApp &operator=(const TestApp &obj) { 
    cout << "= operator\n"; 
    return *this; 
} 

需要注意的是,只有一个const在右侧的操作数,运营商本身和它的返回值是前未声明const

这是错误的申报操作const因为赋值运算符是意味着修改this对象。

而且它不必要地限制了使用操作符返回const引用,因为调用者已经为您提供了非const引用。因此,主叫方已经拥有非const访问对象,所以返回const参考不必要阻止他从一个非const背景

这是当你做的双重分配app1 = app2 = app3;会发生什么重用的返回值:它评估为app1 = (app2 = app3);,因此一个赋值的返回值作为右侧参数传递给下一个赋值。由第一个赋值返回的非const引用可隐式转换为const引用,因此此工作正常。

如果你的编译器抱怨你的2号线与上面给出的声明,则你的编译器是难辞其咎的。我用gcc 4.7.2检查了下面的代码,它工作正常:

class Foo { 
    public: 
     Foo() {}; 
     Foo(const Foo& other) {} 
     Foo& operator=(const Foo& other) { return *this; } 
}; 

int main() { 
    Foo foo1, foo2, foo3; 
    (foo1 = foo2) = foo3; 
    foo1 = foo2 = foo3; 
}