容器及其内容的常量与非常量
const vs non-const of container and its content
对于容器类,如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
的数据成员(指向数据的开始和结束以及分配的缓冲区的结束的三个指针)被而不是通过对其operator[]
的调用而被更改,但该成员不是const
——这不是一个奇怪的设计吗?
还要注意的是,对于原始指针,这两个常数的概念被巧妙地分开,使得相应的原始指针代码
struct B {
B(size_t n) : X(new int[n]) {}
~B() { delete[] X; }
void resize(size_t n); // non-const
int&x(int i) const { return X[i]; } // fine
private:
int*X;
};
效果很好。
那么,在使用std::vector
(不使用mutable
)时,正确/推荐的处理方法是什么?
是中的const_cast<>
吗
int&A::x(int i) const { return const_cast<std::vector<int>&>(X)[i]; }
被认为是可接受的(已知X
是非const
,所以这里没有UB)?
编辑只是为了防止进一步混淆:我确实想修改元素,即容器的内容,而不是容器本身(大小和/或内存位置)。
C++只支持一个级别的const
。就编译器而言有关,它是按位常量:对象(即在sizeof
中计数)在没有玩游戏(const_cast
等),但其他任何事情都是公平的游戏在C++的早期(20世纪80年代末、90年代初)关于bitwise的设计优势进行了大量讨论const与逻辑const(也称为Humpty Dumpty const,因为正如Andy Koenig曾经告诉我的那样,当程序员使用const
,它的意思正是程序员希望它的意思)。一致意见最终一致支持逻辑常量。
这意味着容器类的作者必须选择。容器的元素是容器。如果它们是容器的一部分,那么它们如果容器为常量,则无法修改。没有办法提供选择;容器的作者必须选择另一个。在这里,似乎也有一个共识:元素是容器的一部分,如果容器const,它们不能被修改。(也许与C型数组在这里发挥了作用;如果C型数组是const,则不能修改它的任何元素。)
和你一样,我也遇到过想要禁止的时候修改矢量的大小(也许是为了保护迭代器),但不是它的元素。真的没有令人满意的解决方案;我能想到的最好的事情就是包含mutable std::vector
的新类型,并提供对应const
含义的转发功能在这种特殊情况下,我需要。如果你想区分三个级别(完全常量、部分常量和非常量),你需要推导。基类只公开完全常量和部分常量函数(例如const
int operator[]( size_t index ) const;
和int operator[](
size_t index );
,但不是void push_back( int );
);这个允许插入和移除元件的功能是仅在派生类中公开。不应该的客户insert或remove元素只传递一个非常数引用到基类。
不幸的是,与指针不同,您不能执行类似的操作
std::vector<int> i;
std::vector<const int>& ref = i;
这就是为什么std::vector
不能在这两种可能适用的const
之间消除歧义,而且它必须是保守的。I、 就个人而言,会选择做一些类似的事情
const_cast<int&>(X[i]);
编辑:正如另一位评论者准确指出的那样,迭代器do为这种二分法建模。如果将vector<int>::iterator
存储到开头,那么可以在const方法中取消引用它,并返回一个非常量的int&
。我想。但你必须小心无效。
这不是一个奇怪的设计,这是一个非常深思熟虑的选择,也是正确的选择。
您的B
示例不是std::vector
的好类比,更好的类比是:
struct C {
int& get(int i) const { return X[i]; }
int X[N];
};
但有一个非常有用的区别,那就是可以调整阵列的大小。上面的代码无效,原因与您原来的代码相同。数组(或vector
)元素在概念上是包含类型的"成员"(技术上是子对象),因此您不应该能够通过const
成员函数来修改它们。
我想说const_cast
是不可接受的,除非万不得已,否则也不能使用mutable
。您应该询问为什么要更改const对象的数据,并考虑将成员函数设置为非const。
我建议使用std::vector::at()
方法而不是const_cast
。
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 通过多个头文件使用常量变量
- 在cuda线程之间共享大量常量数据
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 是默认情况下分配给char数组常量的值
- 私有类型的静态常量成员
- OpenGL大的3D纹理(>2GB)非常慢
- 类似枚举的计算常量
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 使用常量键但非常量值进行映射
- 为什么`is_open()`非常常量
- 从getter方法返回常量和非常量值
- 阻止const类函数在引用成员上调用非常常量类函数
- C++初始化非常大的常量数组,最佳实践
- 提高c++中非常大的常量的可读性