Reputation: 7400
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));
}
Upvotes: 3
Views: 132
Reputation: 320621
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.
Upvotes: 2