Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 202
Gael Lorieul Avatar asked Jan 26 '17 17:01

Gael Lorieul


2 Answers

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());
like image 92
Ben Voigt Avatar answered Oct 23 '22 01:10

Ben Voigt


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

like image 27
Serge Ballesta Avatar answered Oct 23 '22 00:10

Serge Ballesta