I am trying to understand at a non-superficial level why the following code does not compile:
#include <vector>
template<typename T>
struct wrapper {
T wrapped_value;
wrapper() {}
template<typename... Args>
wrapper(Args&&... args) : wrapped_value( std::forward<Args>(args)... ) {
}
};
struct A {
int a;
A(int i = 0) : a(i) {
}
};
int main() {
std::vector<wrapper<A>> v1;
std::vector<wrapper<A>> v2;
v1 = v2;
}
I can tell from the error message in the std::vector implementation that the above is failing because the perfect forwarding constructor of wrapper<T> matches the copy constructor. The copy constructor created by substitution into the constructor template would be
wrapper(wrapper<A>& w) : wrapped_value( w ) {
}
Because wrapped_value is of type A this is an error since A does not have a constructor accepting a wrapper<A>.
But isn't "substitution failure not an error"? So the constructor template fails when the compiler attempts to use it as a copy constructor -- why does this block the automatic generation of a copy constructor? Or does it not and the real problem has something to do with the implementation of std::vector?
Also, this is a toy example but what is the best way around this sort of thing in my real code when dealing with classes like this?
- Use "pass-by-value-then-move" rather than perfect forwarding?
- Just define the copy constructor as default?
- Use an std::in_place_t parameter before the variadic parametes in the perfect forwarding constructor?
- Disable the constructor template in the case of copy construction via
enable_ifet. al.