I have encountered an error: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr >' when compile code similar to below using c++ -std=c++14 unique_ptr_vector.cpp -o main
Here is a simplified version:
header file 'my_header.h':
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class A{
public:
A() : n(0) {}
A(int val) : n(val) {}
A(const A &rhs): n(rhs.n) {}
A(A &&rhs) : n(std::move(rhs.n)) {}
A& operator=(const A &rhs) { n = rhs.n; return *this; }
A& operator=(A &&rhs) { n = std::move(rhs.n); return *this; }
~A() {}
void print() const { std::cout << "class A: " << n << std::endl; }
private:
int n;
};
namespace {
std::vector<std::unique_ptr<A>> vecA = {
std::make_unique<A>(1),
std::make_unique<A>(2),
std::make_unique<A>(3),
std::make_unique<A>(4)
};
}
And my src file unique_ptr_vector.cpp
:
#include "my_header.h"
using namespace std;
int main()
{
for(const auto &ptrA : vecA){
ptrA->print();
}
return 0;
}
Do I really need to use push_back(std::make_unique<A>(<some_number>))
individually for each component,
Or what would be a preferred way to populate a container in a header? Or is this a bad idea in general?
I have seen problems around like this one, this one, and this one.
I know now Initialization list seems impossible. but what do people normally do with container<unique_ptr>
. Should I just simply avoid initialize that in a header...
Initialization lists are wrappers around const
arrays.
unique_ptr
s that are const
cannot be moved-from.
We can hack around this (in a perfectly legal way) like this:
template<class T>
struct movable_il {
mutable T t;
operator T() const&& { return std::move(t); }
movable_il( T&& in ): t(std::move(in)) {}
};
template<class T, class A=std::allocator<T>>
std::vector<T,A> vector_from_il( std::initializer_list< movable_il<T> > il ) {
std::vector<T,A> r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
Live example.
Use:
auto v = vector_from_il< std::unique_ptr<int> >({
std::make_unique<int>(7),
std::make_unique<int>(3)
});
If you want to know why initializer lists reference const data, you'll have to track down and read committee minutes or ask someone who was there. I'd guess it is about the principle of least surprise and/or people with bugaboos about mutable data and view types (such as the renaming of array_view
to span
).
If you want more than just vectors:
template<class C, class T=typename C::value_type>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
which still needs massaging to work right with associative containers as we also want to move the key.
template<class VT>
struct fix_vt {
using type=VT;
};
template<class VT>
using fix_vt_t = typename fix_vt<VT>::type;
template<class VT>
struct fix_vt<const VT>:fix_vt<VT>{};
template<class K, class V>
struct fix_vt<std::pair<K,V>>{
using type=std::pair<
typename std::remove_cv<K>::type,
typename std::remove_cv<V>::type
>;
};
template<class C, class T=fix_vt_t<typename C::value_type>>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With