考虑一下
void f(vector<const T*>& p)
{
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
}
以下内容无法编译。事情是vector<T*>
无法转换为vector <const T*>
,这对我来说似乎不合逻辑,因为存在从T*
到{{}的隐式转换1}}。这是为什么?
const T*
也无法转换为vector<const T*>
,但这是预期的,因为const vector <T*>
无法隐式转换为T*
。
答案 0 :(得分:38)
我在代码中添加了几行代码。这足以说明为什么不允许这样做:
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();
}
答案 1 :(得分:22)
vector<T>
和vector<const T>
是不相关的类型。 T
可以转换为const T
的事实并不代表这里的事情。
你必须从类型系统的角度考虑它。实例化vector<int>
与vector<const int>
没有任何共同之处。
答案 2 :(得分:13)
可能值得说明为什么执行所需的转换会违反const-correctness:
#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.
}
问题是vector<int*>.push_back
采用指向非const的指针(从现在开始我称之为“非常量指针”)。这意味着,它可能会修改其参数的指针。特别是在vector的情况下,它可能会将指针移回给修改它的其他人。所以你不能将const指针传递给w的push_back函数,即使模板系统支持它,你想要的转换也是不安全的(它没有)。 const-safety的目的是阻止你将const指针传递给一个带有非const指针的函数,这就是它的工作方式。 C ++要求您具体说明是否要做一些不安全的事情,因此转换肯定不能隐含。事实上,由于模板的工作方式,根本不可能(见后文)。
我认为C ++原则上可以通过允许从vector<T*>&
到const vector<const T*>&
的转换来保持const安全,就像int **
到const int *const *
一样安全。但这是因为定义了矢量的方式:对于其他模板来说,它不一定是安全的。
同样,它理论上可以允许显式转换。事实上,它确实允许显式转换,但仅适用于对象,而不是引用; - )
std::vector<const int*> x(w.begin(), w.end()); // conversion
它不能用于引用的原因是因为模板系统不能支持它。如果允许转换将会破坏的另一个例子:
template<typename T>
struct Foo {
void Bar(T &);
};
template<>
struct Foo<const int *> {
void Baz(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?
答案 3 :(得分:6)
想到这样:
你有两个这样的课:
class V { T* t;};
class VC { T const* t;};
您是否希望这两个课程能够自动转换? 这基本上就是模板类。每种变体都是一种全新的变体。
因此,载体&lt; T *&gt;和矢量&lt; T const *&gt;是完全不同的类型。
我的第一个问题是你真的想存储指针吗?
如果是,我建议看一下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.
}
如果您不需要存储指针,则将对象(而不是指针)存储在容器中。
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.
}
答案 4 :(得分:4)
其他人已经说明了你给出的代码没有编译的原因,但我对如何处理它有不同的答案。我不相信有任何方法可以教编译器如何自动转换这两者(因为这将涉及更改std::vector
的定义)。解决这个烦恼的唯一方法就是做一个明确的转换。
转换为完全不同的向量是不令人满意的(浪费内存和周期应该完全相同)。我建议如下:
#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));
}
我正在使用reinterpret_cast
来声明两件事情是一样的。你应该在使用它之后感觉很脏,但是如果你把它放在一个功能中,并且对那些跟随你的人发表评论,那么就洗一下,尽量保持良好的良心,尽管你将永远(正确地)担心有人从你身下拉出地面。
答案 5 :(得分:3)
正如其他人所说,转换不适用于模板参数。换句话说,
vector<T>
...和
vector<const T>
......是完全不同的类型。
如果你试图在f()方面实现const-correctness而不修改向量的内容,那么这可能更符合你的要求:
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());
}
答案 6 :(得分:1)
除了其他答案之外,还值得阅读C ++ FQA Lite,其中从关键POV讨论了这个(以及许多其他C ++特性): http://yosefk.com/c++fqa/const.html#fqa-18.1
答案 7 :(得分:0)
这就是模板的工作方式 - 没有对模板参数进行任何转换,因此两个向量的类型完全不同。
答案 8 :(得分:0)
模板有点奇怪。从T到U的隐式转换这一事实并不意味着存在从XXX到XXX的隐式转换。它可以实现,但是在模板代码中需要相当多的额外工作来实现它,并且随便,我怀疑在设计std::vector
时这些技术都是已知的(更准确地说,我'我很确定他们不知道。)
编辑:这样的问题是使用迭代器背后的动机的一部分。即使container of X
无法隐式转换为container of const X
,container<X>::iterator
也可以隐式转换为container<X>::const_iterator
。
如果您更换:
void f(vector<const T*>& p) {}
使用:
template <class const_iter>
void f(const_iter b, const_iter e) {}
然后:
int main() {
vector<T*> nonConstVec;
f(nonConstVec.begin(), nonConstVec.end());
return 0;
}
会很好 - 所以会这样:
vector<T const *> constVec;
f(constVec.begin(), constVec.end());
答案 9 :(得分:0)
vector<const T*>
和vector<T*>
都是完全不同的类型。即使你在const T*
内写main()
,你的代码也不会编译。你需要在main中提供专业化。
以下编译:
#include<vector>
using namespace std;
template<typename T>
void f(vector<const T*>& p)
{
}
int main()
{
vector<const int*> nonConstVec;
f(nonConstVec);
}