C++ 向量和常量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2102244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
vector and const
提问by user152508
Consider this
考虑这个
void f(vector<const T*>& p)
{
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
}
The following does not compile.The thing is that vector<T*>
can not be converted to vector <const T*>
, and that seems illogically to me , because there exists implicit conversion from T*
to const T*
. Why is this ?
以下内容无法编译。问题是vector<T*>
无法转换为vector <const T*>
,这对我来说似乎不合逻辑,因为存在从T*
to 的隐式转换const T*
。为什么是这样 ?
vector<const T*>
can not be converted to vector <T*>
too, but that is expected because const T*
can not be converted implicitly to T*
.
vector<const T*>
也不能转换为vector <T*>
,但这是意料之中的,因为 constT*
不能隐式转换为T*
.
回答by MSalters
I've added a few lines to your code. That's sufficient to make it clear why this is disallowed:
我在您的代码中添加了几行。这足以说明为什么不允许这样做:
void f(vector<const T*>& p)
{
static const T ct;
p.push_back(&ct); // adds a const T* to nonConstVec !
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
nonConstVec.back()->nonConstFunction();
}
回答by Nikola Smiljani?
vector<T>
and vector<const T>
are unrelated types. The fact that T
can be converted to const T
doesn't mean a thing here.
vector<T>
并且vector<const T>
是不相关的类型。这个事实T
可以被转换为const T
在这里并不意味着一件事。
You have to think about it from a type system's standpoint. Instantiated vector<int>
doesn't have anything in common with vector<const int>
.
您必须从类型系统的角度考虑它。实例化vector<int>
与vector<const int>
.
回答by Steve Jessop
It may be worth showing why it's a breach of const-correctness to perform the conversion you want:
可能值得说明为什么执行您想要的转换违反了常量正确性:
#include <vector>
const int a = 1;
void addConst(std::vector<const int *> &v) {
v.push_back(&a); // this is OK, adding a const int* to a vector of same
}
int main() {
std::vector<int *> w;
int b = 2;
w.push_back(&b); // this is OK, adding an int* to a vector of same
*(w.back()) = 3; // this is OK, assigning through an int*
addConst(w); // you want this to be OK, but it isn't...
*(w.back()) = 3; // ...because it would make this const-unsafe.
}
The problem is that vector<int*>.push_back
takes a pointer-to-non-const (which I'll call a "non-const pointer" from now on). That means, it might modify the pointee of its parameter. Specifically in the case of vector, it might hand the pointer back out to someone else who modifies it. So you can't pass a const pointer to the push_back function of w, and the conversion you want is unsafe even if the template system supported it (which it doesn't). The purpose of const-safety is to stop you passing a const pointer to a function which takes a non-const pointer, and this is how it does its job. C++ requires you to specifically say if you want to do something unsafe, so the conversion certainly can't be implicit. In fact, because of how templates work, it's not possible at all (see later).
问题是它vector<int*>.push_back
需要一个指向非常量的指针(从现在开始我将称之为“非常量指针”)。这意味着,它可能会修改其参数的指针对象。特别是在向量的情况下,它可能会将指针交还给修改它的其他人。因此,您不能将 const 指针传递给 w 的 push_back 函数,即使模板系统支持它(它不支持),您想要的转换也是不安全的。const-safety 的目的是阻止您将 const 指针传递给采用非常量指针的函数,这就是它的工作方式。C++ 要求你特别说明你是否想做一些不安全的事情,所以转换肯定不能是隐式的。事实上,由于模板的工作方式,这根本不可能(见下文)。
I think C++ could in principle preserve const-safety by allowing a conversion from vector<T*>&
to const vector<const T*>&
, just as int **
to const int *const *
is safe. But that's because of the way vector is defined: it wouldn't necessarily be const-safe for other templates.
我认为 C++ 原则上可以通过允许从vector<T*>&
to转换来保持常量安全性const vector<const T*>&
,就像int **
toconst int *const *
是安全的一样。但这是因为 vector 的定义方式:对于其他模板,它不一定是 const 安全的。
Likewise, it could in theory allow an explicit conversion. And in fact, it does allow an explicit conversion, but only for objects, not references ;-)
同样,理论上它可以允许显式转换。事实上,它确实允许显式转换,但仅适用于对象,而不适用于引用;-)
std::vector<const int*> x(w.begin(), w.end()); // conversion
The reason it can't do it for references is because the template system can't support it. Another example that would be broken if the conversion were allowed:
对于引用它不能做的原因是因为模板系统无法支持它。如果允许转换,另一个例子会被破坏:
template<typename T>
struct Foo {
void Bar(T &);
};
template<>
struct Foo<const int *> {
void Baz(int *);
};
Now, Foo<int*>
doesn't have a Baz function. How on earth could a pointer or reference to Foo<int*>
be converted to a pointer or reference to Foo<const int*>
?
现在,Foo<int*>
没有 Baz 功能。究竟如何将指针或引用Foo<int*>
转换为指向的指针或引用Foo<const int*>
?
Foo<int *> f;
Foo<const int *> &g = f; // Not allowed, but suppose it was
int a;
g.Baz(&a); // Um. What happens? Calls Baz on the object f?
回答by Martin York
Think of like this:
像这样想:
You have two class like this:
你有两个这样的类:
class V { T* t;};
class VC { T const* t;};
Do you expect these two classes to be convertible automatically?
This is basically what a template class is. Each variation is a completely new type.
您是否希望这两个类可以自动转换?
这基本上就是模板类。每个变体都是全新的类型。
Thus vector<T*> and vector<T const*> are completely different types.
因此 vector<T*> 和 vector<T const*> 是完全不同的类型。
My first question is do you really want to store pointers?
我的第一个问题是你真的想存储指针吗?
If yes, I would suggest looking at boost::ptr_container. This holds pointers and deletes them when the vector is destroyed. But more importantly it treats the contained pointers as a normal std:vector treats its contained objects. Thus by making the vector const you can only access its members as const
如果是,我建议查看 boost::ptr_container。这保存指针并在向量被销毁时删除它们。但更重要的是,它将包含的指针视为普通的 std:vector 对待其包含的对象。因此,通过使向量为 const,您只能以 const 的形式访问其成员
void function(boost::ptr_vector<T> const& x)
{
x.push_back(new T); // Fail x is const.
x[4].plop(); // Will only work if plop() is a const member method.
}
If you don't need to store pointers then store the objects (not the pointers) in the container.
如果您不需要存储指针,则将对象(而不是指针)存储在容器中。
void function(std::vector<T> const& x)
{
x.push_back(T()); // Fail x is const.
x[4].plop(); // Will only work if plop() is a const member method.
}
回答by Paul Price
Others have already given the reason why the code you gave doesn't compile, but I have a different answer on how to deal with it. I don't believe there's any way to teach the compiler how to automatically convert the two (because that would involve changing the definition of std::vector
). The only way around this annoyance is to do an explicit conversion.
其他人已经给出了您提供的代码无法编译的原因,但我对如何处理它有不同的答案。我不相信有任何方法可以教编译器如何自动转换两者(因为这将涉及更改 的定义std::vector
)。解决这个烦恼的唯一方法是进行显式转换。
Converting to a completely different vector is unsatisfying (wastes memory and cycles for something that should be completely identical). I suggest the following:
转换为完全不同的向量并不令人满意(对于应该完全相同的东西浪费内存和周期)。我建议如下:
#include <vector>
#include <iostream>
using namespace std;
typedef int T;
T a = 1;
T b = 2;
void f(vector<const T*>& p)
{
for (vector<const T*>::const_iterator iter = p.begin(); iter != p.end(); ++iter) {
cout << **iter << endl;
}
}
vector<const T*>& constify(vector<T*>& v)
{
// Compiler doesn't know how to automatically convert
// std::vector<T*> to std::vector<T const*> because the way
// the template system works means that in theory the two may
// be specialised differently. This is an explicit conversion.
return reinterpret_cast<vector<const T*>&>(v);
}
int main()
{
vector<T*> nonConstVec;
nonConstVec.push_back(&a);
nonConstVec.push_back(&b);
f(constify(nonConstVec));
}
I'm using reinterpret_cast
to declare that the two things are the same. You SHOULDfeel dirty after using it, but if you put it in a function by itself with a comment for those following you, then have a wash and try to continue on your way with a good conscience, though you will always (rightly) have that nagging worry about someone pulling the ground out from under you.
我reinterpret_cast
用来声明这两件事是一样的。使用它后你应该感觉很脏,但是如果你把它单独放在一个函数中,并为那些跟随你的人发表评论,那么洗洗并尝试以良心继续前进,尽管你总是(正确地)有那种担心有人会从你身下拔地而起的唠叨。
回答by John Dibling
As others have said, conversions aren't applied to the template parameters. Put another way,
正如其他人所说,转换不适用于模板参数。换一种方式,
vector<T>
...and:
...和:
vector<const T>
... are completely different types.
...是完全不同的类型。
If you are trying to implement const-correctness in regard to f() not modifying the contents of the vector, this might be more along the lines of what you're looking for:
如果您试图实现关于 f() 的常量正确性而不修改向量的内容,这可能更符合您正在寻找的内容:
void f(vector<T>::const_iterator begin, vector<T>::const_iterator end)
{
for( ; begin != end; ++begin )
{
// do something with *begin
}
}
int main()
{
vector<T> nonConstVec;
f(nonConstVec.begin(), nonConstVec.end());
}
回答by marcin
in addition to other answers, it's worth reading C++ FQA Lite where this (and many others C++ features) are discussed from a critical POV: http://yosefk.com/c++fqa/const.html#fqa-18.1
除了其他答案之外,还值得阅读 C++ FQA Lite,其中从关键 POV 中讨论了这一点(以及许多其他 C++ 特性):http: //yosefk.com/c++fqa/const.html#fqa-18.1
回答by marcin
That's the way templates work - no conversions are applied on the template parameters, so the two vectors are of completely different types.
这就是模板的工作方式 - 没有对模板参数应用转换,因此这两个向量的类型完全不同。
回答by Jerry Coffin
Templates are a bit strange that way. The fact that there's an implicit conversion from T to U doesn't mean that there's an implicit conversion from XXX to XXX. It can be made to happen, but it takes a fair amount of extra work in the template code to make it happen, and offhand, I doubt the techniques were all known when std::vector
was being designed (more accurately, I'm pretty sure they weren't known).
这样的模板有点奇怪。从 T 到 U 的隐式转换并不意味着从 XXX 到 XXX 的隐式转换。它可以实现,但需要在模板代码中进行大量额外的工作才能实现,而且我怀疑这些技术std::vector
在设计时是否都为人所知(更准确地说,我很确定它们不是不知道)。
Edit: Issues like this are part of the motivation behind using iterators. Even though a container of X
isn't implicitly convertible to a container of const X
, a container<X>::iterator
isimplicitly convertible to a container<X>::const_iterator
.
编辑:像这样的问题是使用迭代器背后的动机的一部分。即使 acontainer of X
不能隐式转换为 a container of const X
, acontainer<X>::iterator
也可以隐式转换为 a container<X>::const_iterator
。
If you replace your:
如果您更换您的:
void f(vector<const T*>& p) {}
with:
和:
template <class const_iter>
void f(const_iter b, const_iter e) {}
Then:
然后:
int main() {
vector<T*> nonConstVec;
f(nonConstVec.begin(), nonConstVec.end());
return 0;
}
will be just fine -- and so will:
会很好 - 所以会:
vector<T const *> constVec;
f(constVec.begin(), constVec.end());
回答by Prasoon Saurav
Both vector<const T*>
and vector<T*>
are completely different types. Even if you write const T*
inside your main()
, your code wont compile. You need to provide specialization inside main.
这两个vector<const T*>
和vector<T*>
是完全不同的类型。即使你const T*
在你的 里面写main()
,你的代码也不会编译。您需要在 main 中提供专业化。
The following compiles:
编译如下:
#include<vector>
using namespace std;
template<typename T>
void f(vector<const T*>& p)
{
}
int main()
{
vector<const int*> nonConstVec;
f(nonConstVec);
}