函数调用括号后的限定符适用于成员函数的隐藏this参数:
成员函数void Foo::bar() 有点像这样:void bar(Foo *this)。但是如果 Foo 对象是 const 会发生什么?
struct Foo {
void bar();
};
const Foo f{};
f.bar();
好吧,既然Foo::bar()带了一个Foo *this参数,不允许是const,那么上面的f.bar();就编译失败了。所以我们需要一种方法来限定隐藏的 this 参数,而 C++ 选择这样做的方式是允许这些限定符超出函数括号。
编译器区分这些函数的方式在各个方面都与常规函数重载相同,因为这正是它的本质,尽管语法很奇怪。
此外,const 不是唯一的限定符。还可以添加volatile 限定符,在C++11 中还可以放置左值和右值引用限定符。
我们需要这个函数的两个几乎相同的副本的原因是因为没有直接的方法来找出唯一的区别:不同的返回类型。如果我们有一个 const 对象,并且该对象有一个 getter,它返回对其包含的内容的引用,则该引用需要与整个对象一样限定。
struct Foo {
int i;
int &get_i() const { return i; }
};
int main() {
const Foo f{};
f.get_i() = 10; // i should be const!
}
上面的代码甚至无法编译,因为在 Foo::get_i() const、i 内部是 const,我们不能返回对它的非 const 引用。但如果允许,那就错了,因为我们不应该能够修改 const 对象的成员。所以Foo::get_i() const 需要返回一个对i 的常量引用。
int const &Foo::get_i() const { return i; }
但我们应该能够修改非常量对象的成员,
int main() {
Foo f{};
f.get_i() = 10; // should be fine
}
所以我们不能只有这个功能。当 Foo 对象本身不是 const 时,我们需要一个返回非 const 引用的函数。所以我们根据对象的 const-ness 重载函数:
struct Foo {
int i;
int const &get_i() const { return i; }
int &get_i() { return i; }
};
如果函数体更复杂,有一种可能的选择可以避免这种重复:
struct Foo {
int i;
int const &get_i() const { return i; }
int &get_i() { return const_cast<int &>(const_cast<Foo const *>(this)->get_i()); }
};
也就是说,非 const 重载将其实现委托给 const 重载,使用 const_cast 来修复类型。添加 const 总是安全的。仅当我们确定原始对象不是 const 时,使用 const_cast 删除 const 才是安全的。我们确实知道在这种情况下,因为我们知道我们首先将 const 添加到非 const 对象。