矢量和const

时间:2010-01-20 14:59:03

标签: c++ stl vector const

考虑一下

 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*

10 个答案:

答案 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 Xcontainer<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);
 }