代码之家  ›  专栏  ›  技术社区  ›  user152508

向量和常量

  •  23
  • user152508  · 技术社区  · 15 年前

    考虑这个

     void f(vector<const T*>& p)
     {
     }
     int main()
     { 
      vector<T*> nonConstVec;
      f(nonConstVec);
     }
    

    以下内容无法编译。问题是 vector<T*> 无法转换为 vector <const T*> 在我看来这是不合逻辑的,因为存在着从 T* const T* . 为什么会这样?

    vector<const T*> 无法转换为 vector <T*> 也是,但这是预期的,因为常量 T* 无法隐式转换为 T* .

    10 回复  |  直到 12 年前
        1
  •  38
  •   MSalters    15 年前

    我在您的代码中添加了几行。这足以说明为什么不允许这样做:

    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();
     }
    
        2
  •  22
  •   Nikola Smiljanić    15 年前

    vector<T> vector<const T> 是不相关的类型。事实上 T 可转换为 const T 这不是什么意思。

    你必须从类型系统的角度来考虑它。被实例化 vector<int> 没有任何共同点 vector<const int> .

        3
  •  13
  •   Steve Jessop    15 年前

    可能值得说明为什么执行所需的转换会违反常量正确性:

    #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 获取一个指向非常量的指针(从现在起我将称之为“非常量指针”)。这意味着,它可能会修改其参数的指针。特别是在向量的情况下,它可能会将指针交给修改它的其他人。所以不能将常量指针传递给w的push-back函数,即使模板系统支持(但它不支持),您想要的转换也是不安全的。const safety的目的是阻止您将const指针传递给接受非const指针的函数,这就是它的工作方式。C++要求你具体地说,如果你想做一些不安全的事情,那么转换当然不能是隐性的。事实上,由于模板的工作方式,根本不可能(见下文)。

    我认为C++在原则上可以通过允许转换来保持安全性。 vector<T*>& const vector<const T*>& ,正如 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?
    
        4
  •  6
  •   Loki Astari    15 年前

    这样想:

    你有两个这样的班级:

    class V  { T*       t;};
    class VC { T const* t;};
    

    你希望这两个类能自动转换吗?
    这基本上就是一个模板类。每种变体都是一种全新的类型。

    因此,vector<t*>和vector<t const*>是完全不同的类型。

    我的第一个问题是你真的想存储指针吗?

    如果是,我建议查看boost::ptr_容器。它保存指针,并在向量被破坏时将其删除。但更重要的是,它将包含的指针视为普通的std:vector处理其包含的对象。因此,通过使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.
    }
    
        5
  •  4
  •   Paul Price    13 年前

    其他人已经给出了您给出的代码不能编译的原因,但我对如何处理它有一个不同的答案。我不认为有任何方法可以教编译器如何自动转换这两者(因为这将涉及更改 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 宣布这两件事是相同的。你 应该 使用后感觉脏,但如果你把它放在一个功能本身,并对跟随你的人发表评论,然后洗个澡,试着以一个良好的良心继续你的道路,尽管你总是(正确地)有人从你下面拔出地面的烦躁的担心。

        6
  •  3
  •   John Dibling    15 年前

    正如其他人所说,转换不会应用于模板参数。换个说法,

    vector<T>
    

    …和:

    vector<const T>
    

    …是完全不同的类型。

    如果您试图对f()实现const正确性,而不是修改向量的内容,那么这可能更符合您要查找的内容:

    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());
    }
    
        7
  •  1
  •   marcin    12 年前

    除了其他的答案,值得一读的是C++ FQA Lite,其中从关键POV中讨论了这个(以及许多其他C++特性): http://yosefk.com/c++fqa/const.html#fqa-18.1

        8
  •  0
  •   anon    15 年前

    这就是模板的工作方式——没有对模板参数应用转换,所以这两个向量的类型完全不同。

        9
  •  0
  •   Prasoon Saurav    15 年前

    两者都是 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);
     }
    
        10
  •  0
  •   Jerry Coffin    15 年前

    这样的模板有点奇怪。从t到u的隐式转换并不意味着从xxx到xxx的隐式转换。它是可以实现的,但要实现它,需要在模板代码中做大量的额外工作,而且现在,我怀疑当 std::vector 正在设计中(更准确地说,我很确定他们不知道)。

    编辑:像这样的问题是使用迭代器的动机的一部分。即使是一个 container of X 不能隐式转换为 container of const X A 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());