Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying vector<shared_pt<T>> to vector<shared_ptr<const T>> (different cases) C++

Tags:

c++

c++11

c++14

I have a:

std::vector<std::shared_ptr<T>>

Which I would like to copy to a

std::vector<std::shared_ptr<const T>>

Now I noticed that if I do this:

class A
{
public:
    A(const std::vector<std::shared_ptr<int>>& list) : internalList(list.begin(), list.end()) {}
    std::vector<std::shared_ptr<const int>> internalList;
};

it compiles fine (clang++ std==c++14) but if I do:

class A
{
public:
    A(const std::vector<std::shared_ptr<int>>& list) : internalList(list) {}
    std::vector<std::shared_ptr<const int>> internalList;
};

I find it strange that when I use a copy constructor it doesn't work because it can't figure out the conversion from non-const to const?

xxxx.cpp:672:56: error: no matching constructor for initialization of 'std::vector<std::shared_ptr<const int> >'

Could someone explain why please, and if the way I do it (using iterator in the constructor) is the best solution?

like image 258
user18490 Avatar asked Sep 01 '16 07:09

user18490


2 Answers

Your code could work if std::vector provided a converting constructor:

template<class T, class A = std::allocator<T>>
class vector {
public:
    template<class T1, class A1>
    explicit vector(const vector<T1, A1>& other)
        : vector(other.begin(), other.end())
    {}

    ...
};

But having such a constructor in every Standard Library container doesn't add too much value, because almost the same effect can be achieved by introducing an even more general purpose container_cast utility (see, for example, this answer). Then you could write:

class A
{
public:
    A(const std::vector<std::shared_ptr<int>>& list) : internalList(container_cast(list)) {}
    std::vector<std::shared_ptr<const int>> internalList;
};
like image 189
Leon Avatar answered Nov 10 '22 08:11

Leon


At first the class template instantiated with different types are totally different type at all. Then std::shared_ptr<int> and std::shared_ptr<const int> are totally different types, and std::vector<std::shared_ptr<int>> and std::vector<std::shared_ptr<const int>> are different types too, and can't be converted to each other.

According to the constructors of std::vector, the copy constructor (the 5th one) takes a std::vector with same type as its parameter. It means for std::vector<std::shared_ptr<const int>>, it can't takes std::vector<std::shared_ptr<int>>, which can't be converted to std::vector<std::shared_ptr<const int>> implicitly.

On the other hand the constructor taking iterator range (the 4th one) is function template, the type of the iterator is template parameter, which doesn't have to be the iterator pointing to the same type. It's allowed to be iterator pointing to other type, if this type could be used to construct the vector. std::shared_ptr<int> could be used to construct std::shared_ptr<const int>, then it's fine.

Note that std::shared_ptr have copy/move constructor templates which could take std::shared_ptr with different element type as its argument. (While std::vector doesn't.)

like image 24
songyuanyao Avatar answered Nov 10 '22 09:11

songyuanyao