【问题标题】:return a const reference to an object返回对对象的 const 引用
【发布时间】:2014-03-29 09:50:22
【问题描述】:

我在下面的代码中有冲突。

#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&amp; TestApp::operator=(TestApp&amp;)’ 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 对象不是常量。

【问题讨论】:

标签: c++ reference operator-overloading constants


【解决方案1】:

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

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 &amp;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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多