我可以在向量中使用const来允许添加元素,但不能修改已经添加的元素吗?
我对这个答案的评论让我想到了常量和sorting的问题。 我玩了一下,减less了我的问题,这个代码:
#include <vector> int main() { std::vector <const int> v; }
不会编译 – 你不能创build一个const int的向量。 显然,我应该知道这一点(在智力上我也是),但是我从来没有必要去创造这样的事情。 然而,对我来说,这似乎是一个有用的构造,我想知道是否有任何方法绕过这个问题 – 我想添加东西到一个vector(或其他),但不应该改变一旦添加。
可能有一些令人难堪的简单解决scheme,但这是我以前从未考虑过的事情。 我可能不应该提到sorting(我可能会问另一个问题,请参阅这个问题的困难)。 我真正的基本用例是这样的:
vector <const int> v; // ok (ie I want it to be OK) v.push_back( 42 ); // ok int n = v[0]; // ok v[0] = 1; // not allowed
那么,在C ++ 0x你可以…
在C ++ 03中,有一个段落23.1 [lib.containers.requirements] / 3,它说
存储在这些组件中的对象的types必须满足
CopyConstructible
types(20.1.3)的要求,以及Assignable
types的附加要求。
这是目前阻止你使用const int
作为std::vector
types的参数。
但是,在C ++ 0x中,这个段落丢失了,相反, T
需要是可Destructible
,并且对每个expression式指定了对T
附加要求,例如std::vector
上的v = u
只有在T
是MoveConstructible
和MoveAssignable
。
如果我正确地解释了这些要求,应该可以实例化std::vector<const int>
,你只是会缺less它的一些function(我猜是你想要的)。 您可以通过将一对迭代器传递给构造函数来填充它。 我认为emplace_back()
应该能够工作,尽pipe我没有在T
上find明确的要求。
尽pipe如此,您仍然无法对vector进行sorting。
您放入标准容器的types必须是可复制和可分配的。 auto_ptr
造成这么多麻烦的原因正是因为它不遵循正常的复制和分配语义。 自然,任何const
都不会被赋值。 所以,你不能在一个标准容器中粘贴任何东西。 如果元素不是const
,那么你将能够改变它。
我认为最接近的解决scheme是使用某种间接方法。 所以,你可以有一个指向const的指针,或者你可以拥有一个保存你想要的值的对象,但是这个值不能在对象中改变(就像你用Java中的Integer
得到的那样)。
具有特定索引的元素是不可改变的,这与标准容器的工作方式有关。 你可能能够构build自己的工作方式,但标准的不这样做。 没有一个基于数组的都可以工作,除非你可以设法将它们的初始化符合到{a, b, c}
初始化语法中,因为一旦创build了一个const
数组,就不能改变它。 所以,无论你做什么, vector
类都不可能和const元素一起工作。
在没有某种间接性的情况下在一个容器中存在const
就不能很好地工作。 你基本上要求整个容器是const
– 如果你从一个已经初始化的容器复制它,你可以做到这一点,但是你真的不能有一个容器 – 当然不是一个标准的容器 – 它包含的常量没有某种间接。
编辑 :如果你要做的是大多留下一个容器不变,但仍然能够在代码中的某些地方改变它,然后在大多数地方使用const ref,然后给代码,需要能够改变容器的直接访问权限,或者一个非const的引用会使这个成为可能。
因此,在大多数地方使用const vector<int>&
,然后使用vector<int>&
需要更改容器的位置,或者让代码的这部分直接访问容器。 这样,它几乎是不变的,但你可以改变它,当你想。
另一方面,如果你想几乎总是能够改变容器中的内容,但不改变特定的元素,那么我build议在容器周围放置一个包装类。 在vector
的情况下,包装它,使下标操作符返回一个常量参考,而不是一个非常量参考 – 无论是一个副本。 所以,假设你创build了一个模板化版本,你的下标操作符看起来像这样:
const T& operator[](size_t i) const { return _container[i]; }
这样,您可以更新容器本身,但不能更改它的个别元素。 只要你声明所有的内联函数,它不应该是一个性能打击(如果有的话)的包装。
你不能创build一个const int的向量,即使你可以,它也是无用的。 如果我删除了第二个int,那么从那里开始的所有内容都会向下移动一个 – 读取:modified – 使得不可能保证v [5]在两个不同的场合具有相同的值。
除此之外,一个const声明后就不能赋值,只能抛出const。 如果你想这样做,为什么你首先使用const?
你将需要写你自己的class级。 你当然可以使用std :: vector作为你的内部实现。 然后只需要实现const接口和那些你需要的那些非const函数。
虽然这不能满足您的所有要求(可以sorting),但请尝试一个常量向量:
int values[] = {1, 3, 5, 2, 4, 6}; const std::vector<int> IDs(values, values + sizeof(values));
虽然,你可能想要使用std::list
。 在列表中,值不需要改变,只有链接到他们。 sorting是通过改变链接的顺序来完成的。
你可能不得不花费一些脑力来写自己的东西。 🙁
我将所有的const对象在一个标准的数组。
然后在数组中使用指针向量。
一个小实用程序类只是为了帮助您不必去参考对象和干草。
#include <vector> #include <algorithm> #include <iterator> #include <iostream> class XPointer { public: XPointer(int const& data) : m_data(&data) {} operator int const&() const { return *m_data; } private: int const* m_data; }; int const data[] = { 15, 17, 22, 100, 3, 4}; std::vector<XPointer> sorted(data,data+6); int main() { std::sort(sorted.begin(), sorted.end()); std::copy(sorted.begin(), sorted.end(), std::ostream_iterator<int>(std::cout, ", ")); int x = sorted[1]; }
我与诺亚:用一个只公开你想要允许的类包装向量。
如果你不需要dynamic添加对象的vector,考虑std::tr1::array
。
如果在这个例子中const是非常重要的,我想你可能希望一直使用不可变types。 从概念上讲,你将有一个固定的大小,常量int
的const数组。 任何时候你需要改变它(例如,添加或删除元素,或sorting),你需要做一个数组的副本与执行的操作,并使用它。 虽然这在函数式语言中是非常自然的,但在C ++中看起来不太“正确”。 例如,获得高效的sorting实现可能会非常棘手 – 但是您不会说性能要求是什么。 无论你认为这条路线是否从性能/定制代码的angular度来看是值得的,我相信这是正确的做法。
之后,通过非常量指针/智能指针保存值可能是最好的(但有其自己的开销,当然)。
我一直在想这个问题,似乎你的要求是closures的。
你不想为你的向量添加不可变的值:
std::vector<const int> vec = /**/; std::vector<const int>::const_iterator first = vec.begin(); std::sort(vec.begin(), vec.end()); assert(*vec.begin() == *first); // false, even though `const int`
你真正想要的是你的向量保持一个不变的值集合,以一个可修改的顺序,即使它工作,也不能用std::vector<const int>
语法表示。
恐怕这是一个非常具体的任务,需要一个专门的课程。
的确, Assignable是向量元素types的标准要求之一,并且const int
是不可赋值的。 但是,我期望在一个经过深思熟虑的实现中,编译只有在代码明确依赖于赋值时才会失败。 例如,对于将insert
和erase
std::vector
。
实际上,在许多实现中,即使不使用这些方法,编译也会失败。 例如,Comeau无法编译普通的std::vector<const int> a;
因为相应的std::allocator
不能编译。 它报告std::vector
本身没有直接的问题。
我相信这是一个有效的问题。 如果types参数是const限定的,库提供的实现std::allocator
应该失败。 (我想知道是否有可能做一个std::allocator
的自定义实现来强制整个东西编译。)(这也将是有趣的知道VS如何pipe理编译它)同样,与Comeau std::vector<const int>
编译器出于同样的原因失败std::allocator<const int>
编译失败,根据std::allocator
的规范,编译失败。
当然,在任何情况下,任何实现都有权无法编译std::vector<const int>
因为语言规范允许它失败。
只使用一个未专门的vector
,这是不能做到的。 sorting是通过使用赋值完成的。 所以相同的代码,使这成为可能:
sort(v.begin(), v.end());
…也使这成为可能:
v[1] = 123;
你可以从std :: vector派生一个类const_vector,它重载了任何返回一个引用的方法,并且返回一个const引用。 要做你的sorting,请回到std :: vector。
常量对象的std::vector
可能会由于Assignable
要求而无法编译,因为不能Assignable
常量对象。 移动分配也是如此。 当使用基于vector的地图(如boost flat_map
或Loki AssocVector
时,这也是我经常遇到的问题。 因为它有内部实现std::vector<std::pair<const Key,Value> >
。 因此,根据地图的const key要求几乎是不可能的,这对于任何基于节点的地图来说都很容易实现。
然而,可以看出, std::vector<const T>
是指向量应该存储一个const T
types的对象,或者它只需要在访问时返回一个不可变的接口。 在这种情况下,遵循Assignable / Move Assignable要求的std::vector<const T>
是可能的,因为它存储T
types的对象而不是const T
标准的typedef和allocatortypes需要修改一点,以支持标准requirements.Though支持这样的flat_map
或flat_map
,可能需要在std::pair
接口相当大的变化,因为它直接暴露成员variables第一和第二。
编译失败,因为push_back()
(例如)基本上是
underlying_array[size()] = passed_value;
两个操作数都是T&
。 如果T
是无法工作的const X
const的元素在原理上看起来是正确的,但在实践中它是不自然的,规范并没有说它应该被支持,所以它不存在。 至less不在stdlib中(因为那样它就会在vector中)。