class A
{
public:
A(int i = 25) {x = i;y=new int[i];for(int j=0;j<i;j++) y[j]=j;}
int& f() const {return x;}
int& operator[](int i) const {return y[i];}
private:
int x,*y;
};
int main()
{
A a(15);
cout << a[5];
cout << a.f();
return 0;
}
当我试图编译代码时,它说
"Invalid initialization of reference of type int& from expression of type const int"
关于f()函数。
我理解它,因为它返回对函数声明为const的东西的非const引用。但它不应该与[]重载表现相同吗?
它还返回一个非const引用,该函数声明为const,但它没有显示错误。 有什么区别?
答案 0 :(得分:2)
问题在于:
int& f() const {return x;}
您的函数已标记为const
,因此只能由const
个实例调用。但是,您返回非const
引用,因此如果有效,则可以使用修改const
实例。编译器对此不满意。因此,f()
应该返回const int&
。
另一方面,int& operator[](int) const
编译,因为您返回对指针成员y
指向的数据的引用,但您无法修改指针本身。换句话说,在const
实例上,指针y
为const
,即int * const y
,但不是数据。因此,按位const
- ness被保留,但当然逻辑const
- ness不是,但编译器只关心逐位const
- ness。
要强制执行逻辑const
正确性,一种方法是编写operator[]
的2个版本:
const int& operator[](int i) const {return y[i];}
和
int& operator[](int i) {return y[i];}
请注意,第二个版本应标记为非const
,否则您将尝试重载仅由其返回类型不同的两个函数。如果您想避免非const
版本中的代码重复,您可以通过const
使用const_cast
版本,例如
int& operator[](int i)
{
return const_cast<int&>(const_cast<const A&>(*this)[i]); // use the const version
}
修改强>
有人建议为类似指针的数据成员const
引入propagate_const
传播包装器,这实际上也会使数据指向const
,参见
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4057.pdf
答案 1 :(得分:2)
它告诉您不能从const
成员函数返回对数据成员的非常量左值引用。你需要
const int& f() const {return x;}
如果需要,您可以决定提供非常量重载:
int& f() {return x;}
对于operator[]
,它不会返回对数据成员的引用。您无法通过x
修改y
或operator[]
,因此它实际上是const
成员函数。您可能决定不允许修改y
指向的数据,如果您的类为数组建模,这将是有意义的。但它并非绝对必要,编译器没有理由强制执行它。
const int& operator[](int i) const {return y[i];}
int& operator[](int i) {return y[i];}
答案 2 :(得分:0)
在C ++语言中,类的constness意味着其所有成员的立即const(除了mutable
个)。但是,类的常量不会传播到由引用成员引用的指针成员或指向的数据。指向/引用的数据不是类的一部分,它的常量不受类的常量的影响。
在您的示例中,将成员函数声明为const
会使成员x
和y
被视为const
。但是,y
指向的数据不会变为const
,例如*y
和y[i]
都不是const
。
从封闭类到指向/引用数据的constness的传播实际上是用户意图的一部分。根据您尝试实施的内容,这是您需要或不需要的东西。该语言完全取决于您(或图书馆级解决方案)。