When compiled, the following code causes this error:
'Container::Wrapper::Wrapper(S)': member function already defined or declared
Does the compiler think S&& in the constructor of Wrapper is a forwarding reference?
template<typename T>
struct Container
{
template<class S>
struct Wrapper {
S obj;
Wrapper(S&& obj) : obj(std::forward<S>(obj)){}
Wrapper(const S& obj) : obj(obj){}
};
template<class S>
void push_back(S&& obj) {
void *storage = malloc(sizeof(Wrapper<S>));
new (storage) Wrapper<S>(std::forward<S>(obj));
}
};
struct Foobar{};
int main()
{
Container<Foobar> cont;
Foobar foobar;
cont.push_back(foobar);
return 0;
}
Looking at an example from here, I don't understand how what I'm doing is any different:
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type ⇒ no type deduction;
... // && ≡ rvalue reference
};
Edit:
The solution to this issue was to modify push_back to remove the reference from the type being used to instantiate the Wrapper:
template<class S>
void push_back(S&& obj) {
typedef std::remove_reference<S>::type U;
void *storage = malloc(sizeof(Wrapper<U>));
new (storage) Wrapper<U>(std::forward<S>(obj));
}
There are no universal references involved it the implementation of Wrapper. The only universal reference in your code is Container<>::push_back's parameter.
When you call cont.push_back(foobar);, parameter S of push_back is deduced as Foobar &.
Later you attempt to instantiate your Wrapper<S> with S == Foobar &. Reference collapsing rules dictate that in Wrapper's constructor parameter declarations S && turns into Foobar & and const S & also turns into Foobar &.
This means that you end up with two supposedly "overloaded" Wrapper::Wrapper constructors, which have identical signatures. This is the reason for the error message you observe.
If you attempt to instantiate std::vector with a template argument of lvalue reference type, you will run into exactly the same problem for its push_back overloads. However, with such an attempt compilation will typically fail miserably for other reasons, well before it gets to push_back overloads.
A more distilled example would look as follows
template <typename T> struct MyVector {
void foo(T &&) {}
void foo(const T &) {}
};
int main() {
MyVector<int &> v;
}
and will produce the same error.
The fairly non-obvious part here is that const T & with T = U & actually becomes U & and not const U &. But that's indeed the case.
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