Here's a class with variadic constructor and it's specializations for copy and move from a temporary.
template<class Obj>
class wrapper {
protected:
Obj _Data;
public:
wrapper(const wrapper<Obj>& w): _Data(w._Data) {}
wrapper(wrapper<Obj>&& w):
_Data(std::forward<Obj>(w._Data)) {}
template<class ...Args>
wrapper(Args&&... args):
_Data(std::forward<Args>(args)...) {}
inline Obj& operator()() { return _Data; }
virtual ~wrapper() {}
};
When I use one of specializations like this
wrapper<int> w1(9);
wrapper<int> w2(w1);
I'm getting the error: type of w1
is deduced as int
.
Output from VS2012:
error C2440: 'initializing' : cannot convert from 'win::util::wrapper<int>' to 'int'
How to solve this problem?
You're getting bitten by the greedy perfect forwarding constructor.
wrapper<int> w2(w1);
In the line above, the perfecting forwarding constructor is a better match as compared to the copy constructor, because Args
is deduced as wrapper<int>&
.
A quick fix solution is to change the line above to
wrapper<int> w2(static_cast<wrapper<int> const&>(w1));
this correctly calls the copy constructor but, besides being unnecessarily verbose, doesn't solve the basic problem.
To solve the original problem, you need to conditionally disable the perfect forwarding constructor when Args
is the same as wrapper<Obj>
.
Here's an excellent blog post describing the problem, and how to solve it. To summarize, you need to change the perfect forwarding constructor definition to
template <typename... Args,
DisableIf<is_related<wrapper<Obj>, Args...>::value>...>
wrapper(Args&&... args):
_Data(std::forward<Args>(args)...) {}
where is_related
is defined as
template <typename T, typename... U>
struct is_related : std::false_type {};
template <typename T, typename U>
struct is_related<T, U> : std::is_same<Bare<T>, Bare<U>> {};
and Bare
is
template <typename T>
using Bare = RemoveCv<RemoveReference<T>>;
RemoveCv
and RemoveReference
are alias templates for std::remove_cv
and std::remove_reference
respectively.