Gael Lorieul
Gael Lorieul

Reputation: 3246

Why is it illegal : copying vector of pointers into a vector of pointers to constants

The issue

The following code does not compile in C++11 (nor C++14). I understand the error output from the compiler, but why isn't it allowed by the standard?

//main.cpp

#include <vector>

int main(void)
{
    double a = 3.0;
    double b = 3.0;

    //It works with mere pointers
    const double* ptrToConst = &a;
    /***/ double* ptrToObj   = &a;
//  ptrToObj = ptrToConst; //Illegal : that's understandable…
    ptrToConst = ptrToObj;   //Works

    //But the same doesn't work with vectors to pointers
    std::vector<const double*> ptrsToConst = {&a, &b};
    std::vector</***/ double*> ptrsToObj   = {&a, &b};
//  ptrsToObj = ptrsToConst; //Illegal : that's understandable
    ptrsToConst = ptrsToObj; //Illegal : but why?!
}

The error comes from the line ptrsToConst = ptrsToObj. Indeed, it does not seem possible to copy a vector of pointers std::vector<T*> into a vector of pointers to constants std::vector<const T*>. Note that in both cases, the pointers themselves are not constant.

Why would this operation be illegal? What would be the most elegant work around?


Further details

If I compile by invoking clang++ --std=c++11 main.cpp, the following error message displays:

main.cpp:19:17: error: no viable overloaded '='
    ptrsToConst = ptrsToObj; //Illegal : but why?!
    ~~~~~~~~~~~ ^ ~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:436:7: note: candidate
      function not viable: no known conversion from 'vector<double *, allocator<double *>>' to 'const
      vector<const double *, allocator<const double *>>' for 1st argument
      operator=(const vector& __x);
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:448:7: note: candidate
      function not viable: no known conversion from 'vector<double *, allocator<double *>>' to 'vector<const
      double *, allocator<const double *>>' for 1st argument
      operator=(vector&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:470:7: note: candidate
      function not viable: no known conversion from 'std::vector<double *>' to 'initializer_list<value_type>' (aka
      'initializer_list<const double *>') for 1st argument
      operator=(initializer_list<value_type> __l)
      ^
1 error generated.

Trying the same with gcc (g++) spawns similar error messages.

Apparently, the way vectors are implemented does not allow for the operation I am trying to perform. However that is a safe operation regarding const correctness, right?

Upvotes: 10

Views: 392

Answers (3)

Serge Ballesta
Serge Ballesta

Reputation: 149075

Because it is the way std::vector is implemented: the assignement operator requires the other operand to be of same type. And vector<double *> and vector<const double *> are different type because double and const double are different even if compatible types.

One could imagine to relax that requirement only to compatible types but it would be more complex to implement, when the implementation of standard containers is already complex enough (just read the vector header once...), and neither the writers of the library nor the standard comittee found it necessary.

If you really need it, you will have to write a custom implementation...

Upvotes: 4

Admia
Admia

Reputation: 1144

I am not sure, but maybe c++ copies std::vector<T*> object to std::vector<const T*> object by reference. That means c++ does not allocate new memory for new constructed object of std::vector<const T*>. This new constructed object of std::vector<const T*> would be another name (or reference) for the old object of std::vector<T*>. Then, it makes sense why an error is raised. Because object of std::vector<T*> can alter what it is pointing to, however object of std::vector<const T*> cannot alter what it is pointing to.

Upvotes: -2

Ben Voigt
Ben Voigt

Reputation: 283733

You can do this, just not with operator=. You need the assign member function, which performs the conversion on each individual element.

ptrsToConst.assign(ptrsToObj.begin(), ptrsToObj.end());

Upvotes: 11

Related Questions