在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]; }
//...
};
const int operator[]
确实与int& operator[]
有所不同。
但是怎么样:
int foo() { }
和
const int foo() { }
似乎他们是一样的。
我的问题是,为什么我们使用const int operator[](const int index) const
代替int operator[](const int index) const
?
答案 0 :(得分:84)
忽略非类型返回类型的顶级cv限定符。 这意味着即使你写:
int const foo();
返回类型为int
。如果返回类型是参考,当然,
const
不再是顶级,而是区别:
int& operator[]( int index );
和
int const& operator[]( int index ) const;
很重要。 (另请注意,在函数声明中,如上所述, 任何顶级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
答案 1 :(得分:35)
您应该清楚地区分应用于返回值的const用法,参数和函数本身。
返回值
考虑const std::string SomeMethod() const
。它不允许使用(std::string&&)
函数,因为它需要非const rvalue。换句话说,将始终复制返回的字符串。
const
会保护返回的对象不被修改。<强>参数强>
功能本身
const
,它只能运行其他const
函数,并且不能修改或允许修改类数据。因此,如果它通过引用返回,则返回的引用必须是const。只能在对象上调用const函数或对const本身的对象进行引用。可变字段也可以更改。 this
对T const*
的引用。该函数可以始终const_cast
this
,但当然不应该这样做,并且被视为不安全。<强>结论强>
如果您的方法没有并且永远不会修改类变量,请将其标记为const并确保满足所需的所有标准。它将允许更清晰的代码写入,从而保持 const-correct 。然而,将const
放在任何地方而不给它任何想法肯定不是可行的方法。
答案 2 :(得分:27)
将const
资格添加到非参考/非指针右值, 没有点> em> 将其添加到内置插件中。
如果是 用户定义的类型 ,const
资格认证将阻止来自 的来电者调用非{{1}返回的对象上的成员函数 。例如,给定
const
然后
const std::string foo();
std::string bar();
将被禁止,而
foo().resize(42);
是允许的。
对于像bar().resize(4711);
这样的内置函数,这根本没有意义,因为无论如何都不能修改这些rvalues。
(我记得 Effective C ++ 正在讨论如何制作int
operator=()
引用的返回类型,这是值得考虑的事情。)
<强> 编辑: 强>
似乎 Scott确实提供了这个建议 。如果是这样,那么由于上面给出的原因, 我发现即使对于C ++ 98和C ++ 03 也有问题。 对于C ++ 11,我认为这显然是错误的 ,正如Scott自己似乎已经发现的那样。在errata for Effective C++, 3rd ed.中,他写道(或引用其他抱怨的人):
文本暗示所有按值返回都应该是const,但是非const by-value返回的情况是好的设计并不难找到,例如,返回std :: vector的类型,其中调用者将使用swap一个空的向量,用于“抓取”返回值内容而不复制它们。
后来:
声明按值函数返回值const将阻止它们绑定到C ++ 0x中的右值引用。因为rvalue引用旨在帮助提高C ++代码的效率,所以在指定函数签名时考虑const返回值的交互和rvalue引用的初始化是很重要的。
答案 3 :(得分:17)
你可能会错过迈耶斯的建议。
本质区别在于方法的const
修饰符。
这是一个非const方法(最后注意没有const
),这意味着它可以修改类的状态。
int& operator[](const int index)
这是一个const方法(最后注意const
)
const int operator[](const int index) const
参数类型和返回值如何,int
和const int
之间存在细微差别,但与建议的要点无关。你应该注意的是,非const重载返回int&
,这意味着你可以分配给它,例如num[i]=0
,并且const重载返回不可修改的值(无论返回类型是int
还是const int
)。
在我个人看来,如果某个对象按值传递,则const
修饰符是多余的。此语法更短,并实现相同的
int& operator[](int index);
int operator[](int index) const;
答案 4 :(得分:13)
将值作为const返回的主要原因是您不能说foo() = 5;
之类的内容。这实际上不是原始类型的问题,因为你不能分配给原始类型的rvalues,但它 是用户定义类型的问题(如(a + b) = c;
,带有重载operator+
)。
我总是觉得这样做的理由相当脆弱。你不能阻止那些打算编写尴尬代码的人,而这种特殊类型的强制行为在我看来并没有真正的好处。
使用C ++ 11,这个习惯用法实际上有很多伤害:将值作为const返回会阻止移动优化,因此应尽可能避免使用。基本上,我现在认为这是一种反模式。
这是关于C ++ 11的tangentially related article。
答案 5 :(得分:9)
编写该书时,该建议几乎没用,但确实可以阻止用户编写,例如foo() = 42;
,并希望它能够改变持久性。
对于operator[]
,如果您还没有提供非const
重载来返回非const
引用,这可能会有点混乱,尽管您可能也许通过返回const
引用或代理对象而不是值来防止这种混淆。
现在,这是一个糟糕的建议,因为它会阻止您将结果绑定到(非const
)右值参考。
(正如评论中指出的那样,在返回像int
这样的原始类型时问题没有实际意义,因为语言会阻止你分配给这种类型的rvalue
;我正在谈论关于更一般的情况,包括返回用户定义的类型。)
答案 6 :(得分:2)
对于原始类型(如int
),结果的常数无关紧要。对于类,它可能会改变行为。例如,您可能无法对函数的结果调用非const方法:
class Bigint {
const C foo() const { ... }
...
}
Bigint b;
b.foo().bar();
如果bar()
是C
的const成员函数,则禁止上述操作。
一般来说,选择合理的。
答案 7 :(得分:0)
看看:
const int operator[](const int index) const
声明结尾的const
。它描述了可以在常量上调用此方法。
另一方面,当你只写
时int& operator[](const int index)
只能在非const实例上调用它,并提供:
big_int[0] = 10;
语法。
答案 8 :(得分:0)
您的一个重载是将引用返回到数组中的某个项目,然后您就可以对其进行更改。
int& operator[](const int index) { return _data[index]; }
另一个重载是返回一个值供你使用。
const int operator[](const int index) const { return _data[index]; }
由于您以相同的方式调用每个重载,并且在使用它时永远不会更改该值。
int foo = myBigInt[1]; // Doesn't change values inside the object.
myBigInt[1] = 2; // Assigns a new value at index `
答案 9 :(得分:0)
在数组索引运算符(operator[]
)的示例中,它确实有所作为。
使用
int& operator[](const int index) { /* ... */ }
您可以使用索引直接更改数组中的条目,例如像这样使用它:
mybigint[3] = 5;
第二个const int operator[](const int)
运算符仅用于获取值。
但是,作为函数的返回值,对于简单类型,例如int
没关系。当它重要时,如果你要返回更复杂的类型,比如std::vector
,并且不希望函数的调用者修改向量。
答案 10 :(得分:0)
此处const
返回类型并不那么重要。由于int temporaries不可修改,因此使用int
vs const int
时没有可观察到的差异。如果你使用了一个可以修改的更复杂的对象,你会发现不同。
答案 11 :(得分:0)
围绕这两个版本的技术性,有一些很好的答案。对于原始值,它没有什么区别。
然而,我一直认为const
是程序员而不是编译器。当你写const
时,你明确地说“这不应该改变”。让我们面对现实,const
无论如何通常都可以绕过它,对吗?
当你返回const
时,你告诉程序员使用该函数的值不应该改变。如果他正在改变它,他可能做错了什么,因为他/她不应该这样做。
编辑:我也认为“尽可能使用const
”是不好的建议。你应该在有意义的地方使用它。