26

可能重复:
我应该返回 const 对象吗?
(那个问题的原标题是:int foo() 还是 const int foo()?解释我为什么错过它。)


有效的 C++,第 3 项:尽可能使用 const。特别是,返回 const 对象被提升以避免像if (a*b = c) {. 我觉得这有点偏执,但我一直在遵循这个建议。

在我看来,返回 const 对象会降低 C++11 的性能。

#include <iostream>
using namespace std;

class C {
public:
    C() : v(nullptr) { }

    C& operator=(const C& other) {
        cout << "copy" << endl;
        // copy contents of v[]
        return *this;
    }

    C& operator=(C&& other) {
        cout << "move" << endl;
        v = other.v, other.v = nullptr;
        return *this;
    }

private:
    int* v;
};

const C const_is_returned() { return C(); }

C nonconst_is_returned() { return C(); }

int main(int argc, char* argv[]) {
    C c;
    c = const_is_returned();
    c = nonconst_is_returned();
    return 0;
}

这打印:

copy
move

我是否正确实施了移动分配?或者我根本不应该在 C++11 中返回 const 对象?

4

3 回答 3

26

返回const对象是一种可能会导致其他问题的解决方法。从 C++11 开始,对于赋值问题有一个更好的解决方案:Reference Qualifiers for member functions。我尝试用一​​些代码来解释它:

int foo(); // function declaration
foo() = 42; // ERROR

int第二行中的赋值导致C 和 C++ 中内置类型的编译时错误。其他内置类型也一样。这是因为内置类型的赋值运算符需要左侧的非常量左值引用。将其放入代码中,赋值运算符可能如下所示(无效代码):

int& operator=(int& lhs, const int& rhs);

在 C++ 中总是可以将参数限制为左值引用。*this但是,对于成员函数 ( )的隐式第一个参数,这在 C++11 之前是不可能的。

C++11 改变了这一点:与成员函数的const 限定符类似,现在有成员函数的引用限定符。以下代码显示了复制和移动运算符的用法(注意&参数列表后面):

struct Int
{
    Int(const Int& rhs) = default;
    Int(Int&& rhs) noexcept = default;
    ~Int() noexcept = default;
    auto operator=(const Int& rhs) & -> Int& = default;
    auto operator=(Int&& rhs) & noexcept -> Int& = default;
};

使用此类声明,以下代码片段中的赋值表达式无效,而分配给局部变量有效 - 就像在第一个示例中一样。

Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded '='

所以不需要返回 const 对象。您可以将赋值运算符限制为左值引用,以便其他一切仍按预期工作 - 特别是移动操作。

也可以看看:

于 2012-10-27T12:01:17.107 回答
25

可以说,按值返回const对象从来都不是一个好主意,即使在 C++11 之前也是如此。它的唯一效果是它阻止调用者在返回的对象上调用非常量函数——但这并不是很相关,因为调用者无论如何都收到了对象的副本

虽然返回一个常量对象确实可以防止调用者错误地使用它(例如,错误地进行赋值而不是比较),但函数不应该负责决定调用者如何使用返回的对象(除非返回的对象是函数拥有的结构的引用或指针)。函数实现者不可能知道返回的对象是用于比较还是用于其他用途。

您也对,在 C++11 中问题更加严重,因为返回 aconst可以有效地防止移动操作。(不过,它并不能防止复制/移动省略。)

当然,同样重要的是要指出const当函数返回引用或指针时它仍然非常有用(并且使用它没有偏执的迹象)。

于 2012-10-27T12:12:49.517 回答
3

您调用const_is_returned触发器的原因copy constructor而不是必须修改对象move constructor的事实,move因此它不能用于const对象。我倾向于说const在任何情况下都不建议使用,应该由程序员判断,否则你会得到你演示的东西。好问题。

于 2012-10-27T11:59:51.250 回答