返回对象的const引用

时间:2014-03-29 09:50:22

标签: c++ reference operator-overloading 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对象不是常数之后。

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:2)

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

const int x = 10;
x = 20;

编译器将为第二个语句发出错误,因为x是一个常量对象,可能无法分配。

同样适用于陈述

(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

当然,您可能将这两个运算符放在一起,但没有任何意义可以使用第二个运算符。

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