容器的const正确性

7
多年来,我们一直盲目地认为std::vector<T>::operator[] const返回const_reference。但考虑到智能指针中const的工作方式,我开始怀疑为什么STL容器会被设计成这样。似乎一个const std::vector的"constness"适用于向量本身和其中的元素,而对于智能指针,"constness"仅适用于指针而不是所指向的元素。
为了澄清,似乎应该有一个类似向量的容器,其中const只意味着用户无法更改容器的大小,但容器中的元素是可变的。我的主要问题是:是否有任何因素阻止将这种类型的容器定义为“const correct”?
看起来有一些hackish解决方法,通过添加额外的间接层(例如std::vector<std::unique_ptr<T>> const)来实现这一点,但我想找到一些在维护方面不那么笨拙的东西。
顺便说一下,如果在STL容器之前将智能指针合并到语言中,今天的const访问器还会被定义成这样吗?

你可以定义自己的类,使用组合来间接地操作一个可变的std::vector,并通过公共成员函数确保const正确性(在你的意义上)。 - oLen
2
std::vector<T>::operator[] 返回一个引用。而 std::vector<T>::operator[] const 则返回一个常量引用。 - Marshall Clow
@Marshall Clow 不知怎么的,这个被我漏掉了。我会把它加回去的。谢谢。 - pelletjl
2个回答

0
澄清一下,似乎应该有一个类似向量的容器,其中const只意味着用户无法更改容器的大小,但容器中的元素是可变的。
这就是std :: array。您可以在编译时设置大小。对于在构造函数时间设置大小,有建议的dynarray。

对于向量来说这似乎是合理的,但其他STL容器呢?像std::map这样的容器似乎也很合理,其中你不能添加或删除键,但值是可变的。 - pelletjl
2
标准库不是所有可能事物的集合。 - Marshall Clow
由于constness并不意味着在编译时已知值,因此我更愿意将新的数组跨度/视图与可变元素的非可调整容器进行比较。但出于我不知道的原因,这些视图具有与“vector”相同的行为:它们的操作的行为就像视图是其元素 - operator==比较元素,并且const是深层次的,而不是它们包装的指针。 - dyp

0

这个怎么样?

#include <vector>

template<class T>
class mut_wrapper {
    mutable T value;
public:
    template<class... Args>
    mut_wrapper(Args... args) : value(args...) {}
    operator T&() const { return value; }
};

template<class T>
using mut_vector = std::vector<mut_wrapper<T>>;

void try_modify_value(const mut_vector<int> &v) {
    ++v[0];
}

// doesn't compile
// void try_push(const mut_vector<int> &v) {
//     v.push_back(3);
// }

void try_push_mutable(mut_vector<int> &v) {
    v.push_back(3);
}

前置构造函数和隐式转换使得mut_wrapper<T>的行为与T&完全相同,而类型别名使得mut_vector易于使用。
根据要求,const mut_vector<T>的值可以改变其元素,但不能添加或删除元素。
这种方法也适用于其他STL容器,但不应该用于有序或散列数据结构中的键(例如unordered_mapset的键),原因很明显。此外,它不会产生额外的存储开销或不必要的指针间接操作,就像使用unique_ptr一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接