C++ 容器及其内容的常量与非常量
对于容器类,例如C++ 容器及其内容的常量与非常量,c++,constants,containers,C++,Constants,Containers,对于容器类,例如std::vector,有两个不同的常量概念:容器的常量(即其大小)和元素的常量。似乎std::vector混淆了这两者,因此以下简单代码无法编译: struct A { A(size_t n) : X(n) {} int&x(int i) const { return X[i]; } // error: X[i] is non-const. private: std::vector<int> X; }; 很好用 那么,在使用std::ve
std::vector
,有两个不同的常量概念:容器的常量(即其大小)和元素的常量。似乎std::vector
混淆了这两者,因此以下简单代码无法编译:
struct A {
A(size_t n) : X(n) {}
int&x(int i) const { return X[i]; } // error: X[i] is non-const.
private:
std::vector<int> X;
};
很好用
那么,在使用std::vector
(不使用mutable
)时,正确/推荐的处理方法是什么
是一个const\u cast
,如中所示
int&A::x(int i) const { return const_cast<std::vector<int>&>(X)[i]; }
int&A::x(inti)const{return const_cast(x)[i];}
视为可接受(X
已知为非常量,因此此处无UB)
编辑只是为了防止进一步混淆:我确实想修改元素,即容器的内容,但不是容器本身(大小和/或内存位置)。这不是一个奇怪的设计,这是一个非常慎重的选择,而且是正确的选择 对于
std::vector
,您的B
示例不是一个很好的类比,更好的类比是:
struct C {
int& get(int i) const { return X[i]; }
int X[N];
};
但是非常有用的区别是数组可以调整大小。上述代码无效的原因与原始代码相同,即数组(或vector
)元素在概念上是包含类型的“成员”(技术上是子对象),因此您不能通过const
成员函数修改它们
我想说,const_cast是不可接受的,除非作为最后手段,否则也不能使用
mutable
。你应该问为什么要改变一个const对象的数据,并考虑让成员函数非const .< /p> 不幸的是,不像指针,你不能做像之类的事情。
std::vector<int> i;
std::vector<const int>& ref = i;
const_cast<int&>(X[i]);
编辑:正如另一位评论者准确指出的那样,迭代器确实模拟了这种二分法。如果在开头存储了一个
vector::iterator
,则可以在const方法中反引用它,并返回一个非constint&
。我想。但是您必须小心无效。我建议使用std::vector::at()
方法,而不是const\u cast
C++只支持一级const
。至于编译器
值得注意的是,它是按位常量:“位”实际上在
对象(即计数在sizeof
中)在没有
玩游戏(const_cast
等),但其他一切都是公平的
游戏在C++早期(20世纪80年代末,90年代初)
关于bitwise的设计优势进行了大量讨论
常量vs.逻辑常量(也称为Humpty-Dumpty常量,
因为正如Andy Koenig曾经告诉我的,当程序员使用
const
,它的意思正是程序员想要它的意思)。
一致意见最终合并为支持逻辑常数
这确实意味着容器类的作者必须
选择。容器的元素是
容器,或不是。如果它们是容器的一部分,那么它们
如果容器为常量,则无法修改。没有办法
提供选择;容器的作者必须选择一个或多个
其他的。在这方面,似乎也有一个共识:即
元素是容器的一部分,如果容器是
常量,它们不能被修改。(也许与之类似
C样式数组在这里起作用;如果C样式数组是常量,
则无法修改其任何元素。)
和你一样,我也遇到过想禁止的时候
修改载体的大小(可能是为了保护
迭代器),但不是它的元素。真的没有
满意的解决方案;我能想到的最好办法就是创造
一种新类型,它包含一个可变std::vector
,并提供
与const
在这种特殊情况下,我需要。如果你想区分
三个级别(完全常量、部分常量和非常量),
你需要推导。基类只公开
完全常量和部分常量函数(例如aconst
整数运算符[](大小索引)常量;
和整数运算符[](
大小(索引);
,但不是void push_-back(int);
);这个
允许插入和删除元素的函数包括
仅在派生类中公开。不应该的客户
插入或删除元素仅传递一个非常量引用
到基类。可以通过调用
操作符[]
来更改std::vector
的数据。正如您所写的A::X
,A.X(1)+
是完全合法的,并修改向量的内容。@DavidSchwartz向量的内容不是它的实际数据(尽管您可以在逻辑上将它们关联起来)。如果检查std::vector
,它只有3个指针作为数据(数据的开始和结束以及缓冲区的结束)。这些都没有改变,这是一个解释问题。如果你把一个向量
看作是一个可重新调整大小的C数组,那么我同意。但是,resize()
etc函数在哪里适合这种类比?它们必须不止是常量:您希望能够为元素提供非常量访问权限,但不能为大小提供非常量访问权限。这在std::vector
中是不可能的。我认为解决这一问题的通常方法是提供迭代器,迭代器通常允许更改内容,但不允许更改容器。这只是一个类比,不要太字面化,反正resize
不是常量,所以允许对对象进行变异。我不确定你说的迭代器是什么意思,但是没有一个标准容器会给你一个常量容器的非常量迭代器。我说的迭代器是,如果你有一个(非常量)迭代器,你就不能修改容器。迭代器的使用提出了一个有趣的解决方案:一个视图类,它只包含开始迭代器和结束迭代器,只提供有限的函数
const_cast<int&>(X[i]);