我应该返回const对象吗?

abc*_*987 76 c++

Effective C++第03项中,尽可能使用const.

class Bigint
{
  int _data[MAXLEN];
  //...
public:
  int& operator[](const int index) { return _data[index]; }
  const int operator[](const int index) const { return _data[index]; }
  //...
};
Run Code Online (Sandbox Code Playgroud)

const int operator[]确实有所作为int& operator[].

但是关于:

int foo() { }
Run Code Online (Sandbox Code Playgroud)

const int foo() { }
Run Code Online (Sandbox Code Playgroud)

似乎他们是一样的.

我的问题是,为什么我们用const int operator[](const int index) const而不是int operator[](const int index) const

Jam*_*nze 85

忽略非类型返回类型的顶级cv限定符.这意味着即使你写:

int const foo();
Run Code Online (Sandbox Code Playgroud)

返回类型是int.如果返回类型是引用,当然,const不再是顶级,并且区别在于:

int& operator[]( int index );
Run Code Online (Sandbox Code Playgroud)

int const& operator[]( int index ) const;
Run Code Online (Sandbox Code Playgroud)

很重要.(另请注意,在函数声明中,如上所述,任何顶级cv限定符也会被忽略.)

区别也与类类型的返回值有关:如果返回T const,则调用者不能对返回的值调用非const函数,例如:

class Test
{
public:
    void f();
    void g() const;
};

Test ff();
Test const gg();

ff().f();             //  legal
ff().g();             //  legal
gg().f();             //  **illegal**
gg().g();             //  legal
Run Code Online (Sandbox Code Playgroud)

  • ff和gg被声明为返回Test的函数.如果是这样,invokations应该是ff().f()等...如果ff是Test类的一个实例,则应该从声明中删除括号(+但是:) (6认同)
  • ** - 1**"忽略非类型返回类型的顶级cv限定符"不正确.它们仍然是函数类型的一部分.但是,忽略了非类型的prvalue的cv限定,因此调用返回`int const`的函数,相当于调用该函数调用以返回`int`.在C++11§3.10/ 4中的标准,"类prvalues可以有cv限定类型; 非类prvalue总是有cv-unqualified类型". (3认同)

Bar*_*icz 35

您应该清楚地区分应用于返回值的const用法,参数和函数本身.

返回值

  • 如果函数按值返回,则const无关紧要,因为正在返回对象的副本.然而,在C++ 11中,它涉及移动语义.
  • 对于基本类型也不重要,因为它们总是被复制.

考虑const std::string SomeMethod() const.它不允许使用该(std::string&&)函数,因为它需要非const rvalue.换句话说,将始终复制返回的字符串.

  • 如果函数通过引用返回,则const保护返回的对象不被修改.

参数

  • 如果按值传递参数,则const会阻止函数中的函数修改给定值.无论如何都无法修改参数中的原始数据,因为您只有副本.
  • 请注意,由于始终创建副本,因此const仅对函数体有意义,因此,仅在函数定义中检查它,而不在声明(接口)中检查.
  • 如果通过引用传递参数,则应用与返回值相同的规则

功能本身

  • 如果函数const在最后,它只能运行其他const函数,并且不能修改或允许修改类数据.因此,如果它通过引用返回,则返回的引用必须是const.只能在对象上调用const函数或对const本身的对象进行引用.可变字段也可以更改.
  • 编译器创建的行为更改this对引用的引用T const*.该功能可以随时使用const_cast this,但当然不应该这样做并且被认为是不安全的.
  • 在类方法上只使用这个说明符当然是明智的.最后使用const的全局函数将引发编译错误.

结论

如果您的方法没有并且永远不会修改类变量,请将其标记为const并确保满足所需的所有标准.它将允许编写更清晰的代码,从而保持其正确性.然而,把const它放在任何地方而不给它任何想法肯定不是要走的路.

  • 两个更正:首先,对于非类型**,对于按值返回,顶级`const`被忽略**.它对类类型很重要.对于按值传递的参数,在函数声明中忽略顶级`const`.它只在定义中有意义. (4认同)

sbi*_*sbi 29

没有什么价值的加入const资格的非参考/非指针右值,并没有一点在其添加到内置插件.

对于用户定义的类型,const限定条件将阻止调用者在返回的对象上调用非const成员函数.例如,给定

const std::string foo();
      std::string bar();
Run Code Online (Sandbox Code Playgroud)

然后

foo().resize(42);
Run Code Online (Sandbox Code Playgroud)

会被禁止,而

bar().resize(4711);
Run Code Online (Sandbox Code Playgroud)

会被允许的.

对于内置int函数来说,这根本没有意义,因为无论如何都不能修改这样的rvalues.

(我记得C++有效讨论作出的返回类型operator=()一个const参考,不过,这是值得考虑的问题.)


编辑:

看来,斯科特确实给他的意见.如果是这样,那么由于上面给出的原因,我发现即使对于C++ 98和C++ 03也有问题.对于C++ 11,我认为这显然是错误的,正如Scott自己似乎已经发现的那样.在Effective C++勘误表中,第3版.,他写道(或引用其他抱怨的人):

该文本暗示所有按值返回应该是const,但非常量按值返回是良好设计的情况并不难找到,例如,返回std :: vector类型,其中调用者将使用带有空向量的swap "抓取"返回值内容而不复制它们.

然后:

声明按值函数返回值const将阻止它们绑定到C++ 0x中的右值引用.因为rvalue引用旨在帮助提高C++代码的效率,所以在指定函数签名时考虑const返回值的交互和rvalue引用的初始化是很重要的.

  • @sehe:我给斯科特发了一封邮件询问这个问题,他的答复是那些勘误评论反映了他目前对这个问题的看法. (2认同)

And*_*rey 17

您可能会错过Meyers的建议.本质区别在于const方法的修饰符.

这是一个非const方法(最后注意没有const),这意味着允许修改类的状态.

int& operator[](const int index)
Run Code Online (Sandbox Code Playgroud)

这是一个const方法(最后的注意const)

const int operator[](const int index) const
Run Code Online (Sandbox Code Playgroud)

参数的类型和返回值如何,int和之间存在细微差别const int,但与建议的要点无关.你应该注意的是,非const超载的回报int&,这意味着你可以分配给它,比如num[i]=0,和常量超载返回不可修改的值(无论返回类型intconst int).

在我个人看来,如果一个对象按值传递,则const修饰符是多余的.此语法更短,并实现相同

int& operator[](int index);
int operator[](int index) const;
Run Code Online (Sandbox Code Playgroud)

  • 实际上,问题是关于返回一个`const`值. (5认同)

Ker*_* SB 13

将值作为const返回的主要原因是你不能说出类似的东西foo() = 5;.这实际上不是原始类型的问题,因为您无法分配基本类型的rvalues,但它用户定义类型的问题(例如(a + b) = c;,带有重载operator+).

我一直觉得这样做的理由相当脆弱.你无法阻止那些打算编写尴尬代码的人,而这种特殊类型的强制在我看来并没有真正的好处.

对于C++ 11,这个习惯用法实际上有很多伤害:将值作为const返回会阻止移动优化,因此应尽可能避免使用.基本上,我现在认为这是一种反模式.

这是一篇关于C++ 11的相关文章.


Mik*_*our 9

在编写该书时,该建议几乎没用,但确实可以阻止用户编写,foo() = 42;并期望它能够改变持久性的东西.

在这种情况下operator[],如果您还没有提供const返回非const引用的非重载,这可能会有点混乱,尽管您可以通过返回const引用或代理对象而不是值来防止这种混淆.

这些天,这是一个糟糕的建议,因为它阻止您将结果绑定到(非const)右值引用.

(正如评论中所指出的那样,回归原始类型时问题是没有意义的int,因为语言阻止你分配给rvalue这样的类型;我在谈论包括返回用户定义类型的更一般的情况. )

  • 他的例子返回了`int`.`foo()= 42;`是非法的,无论返回类型是声明为`int`还是`int const`.我不认为Scott在函数声明中返回类型和参数这样的地方争论使用`const`,编译器会忽略它. (10认同)