vector and const

后端 未结 10 1566
無奈伤痛
無奈伤痛 2020-12-03 07:13

Consider this

 void f(vector& p)
 {
 }
 int main()
 { 
  vector nonConstVec;
  f(nonConstVec);
 }

The followi

相关标签:
10条回答
  • 2020-12-03 07:32

    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.

    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);
     }
    
    0 讨论(0)
  • 2020-12-03 07:35

    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.

    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 SHOULD feel 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.

    0 讨论(0)
  • 2020-12-03 07:36

    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).

    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.

    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 *> 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?
    
    0 讨论(0)
  • 2020-12-03 07:47

    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

    0 讨论(0)
  • 2020-12-03 07:51

    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).

    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 is implicitly convertible to 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());
    
    0 讨论(0)
  • 2020-12-03 07:52

    That's the way templates work - no conversions are applied on the template parameters, so the two vectors are of completely different types.

    0 讨论(0)
提交回复
热议问题