对于容器类,例如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?
编辑只是为了防止进一步的混淆:我确实想要修改元素,即容器的内容而不是容器本身(大小和/或内存)位置)。
答案 0 :(得分:17)
C ++仅支持一级const
。至于编译器
关注的是,它是按位const:实际上是“位”
如果没有,则无法修改对象(即在sizeof
中计算)
玩游戏(const_cast
等),但其他任何事情都是公平的
游戏。在C ++的早期(20世纪80年代末,90年代初)
对于按位的设计优势进行了大量讨论
const与逻辑const(也称为Humpty-Dumpty const,
因为正如Andy Koenig曾经告诉我的那样,当程序员使用时
const
,这意味着程序员想要它的意思。
最终的共识有利于逻辑const。
这确实意味着容器类的作者必须制作 一个选择。是容器的元素的一部分 容器,或不。如果他们是容器的一部分,那么他们 如果容器是const,则无法修改。没有办法 提供选择;容器的作者必须选择一个或 另一个。在这里,似乎有一个共识: 元素是容器的一部分,如果容器是 const,它们无法修改。 (也许与...平行 C风格阵列在这里发挥了作用;如果C样式数组是const, 然后你不能修改它的任何元素。)
和你一样,我曾经遇到过想要禁止的时候 修改矢量的大小(也许是为了保护 迭代器),但不包括其元素。没有真的 满意的解决我能想到的最好的就是创造 一种新类型,包含mutable std::vector
,并提供
转发功能,对应const
的含义
我需要在这个具体案例中。如果你想区分
三个级别(完全const,部分const和非const),
你需要推导。基类只暴露了
完全const和部分const函数(例如const
int operator[]( size_t index ) const;
和int operator[](
size_t index );
,但不是void push_back( int );
);该
允许插入和移除元素的功能是
仅在派生类中公开。不应该的客户
insert或remove元素只传递一个非const引用
到基类。
答案 1 :(得分:4)
不幸的是,与指针不同,你不能做像
这样的事情std::vector<int> i;
std::vector<const int>& ref = i;
这就是为什么std::vector
无法消除它们可能适用的两种const
之间的歧义,而且它必须是保守的。我个人会选择做像
const_cast<int&>(X[i]);
编辑:正如另一位评论者准确指出的那样,迭代器做模拟这种二分法。如果您将vector<int>::iterator
存储到开头,则可以在const方法中取消引用它并返回非const int&
。我认为。但是你必须小心失效。
答案 2 :(得分:3)
这不是一个奇怪的设计,它是一个非常慎重的选择,而且是正确的恕我直言。
您的B
示例不是std::vector
的一个很好的类比,更好的比喻是:
struct C {
int& get(int i) const { return X[i]; }
int X[N];
};
但是有一个非常有用的区别,即数组可以调整大小。由于与原始代码相同的原因,上面的代码无效,数组(或vector
)元素在概念上是包含类型的“成员”(技术上是子对象),因此您不应该修改它们通过const
成员函数。
我会说const_cast
是不可接受的,除非作为最后的手段,否则它们都不会使用mutable
。您应该问为什么要更改const对象的数据,并考虑使成员函数为非const。
答案 3 :(得分:-1)
我建议使用std::vector::at()
方法而不是const_cast
。