Using MSVC2010…
I have a struct that wraps a std::string, with standard move ctor’s defined, as well as a perfect forwarding ctor to forward an argument to the std::string ctor.
struct Wrapper
{
std::string value;
Wrapper()
{
}
Wrapper( Wrapper const& rhs )
:value(rhs.value)
{
}
Wrapper( Wrapper&& rhs )
:value(std::move(rhs.value))
{
}
Wrapper& operator=( Wrapper const& rhs )
{
value = rhs.value;
return *this;
}
Wrapper& operator=( Wrapper&& rhs )
{
value = std::move(rhs.value);
return *this;
}
template<typename StringT>
Wrapper( StringT&& value )
:value(std::forward<StringT>(value))
{
}
};
However now it seems I cannot copy-construct a Wrapper from another wrapper
Wrapper w0;
Wrapper w1(w0);
This results in a compile error pointing at the perfect forwarding ctor, saying it cannot convert a Wrapper to a std::string. Is this correct behavior? Shouldn’t the compiler call the copy ctor, versus the templated overload?
1>t:\depot\warp\code\apps\pf_test\main.cpp(56): error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax> &'
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>,
1> _Ax=std::allocator<char>
1> ]
1> Reason: cannot convert from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax>'
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>,
1> _Ax=std::allocator<char>
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> t:\depot\warp\code\apps\pf_test\main.cpp(63) : see reference to function template instantiation 'Wrapper::Wrapper<Wrapper&>(StringT)' being compiled
1> with
1> [
1> StringT=Wrapper &
1> ]
1>
If I define another copy ctor, one that takes a non-const reference to Wrapper (shown below), then everything seems fine… Is this the way moving forward? Or did I mess something up? Or is this a bug in VS2010?
Wrapper( Wrapper& rhs )
:value(rhs.value)
{
}
Yes, this is the correct behaviour.
There are two constructors that might be called in this case – your copy constructor and the template one. However, template constructor might deduce the type to be perfect match (
Wrapper(StringT&&)withStringT = Wrapper&yields perfect matchWrapper(Wrapper&)) and thus gets used instead of copy constructor.Suggested workaround – use
std::enable_iffrom<type_traits>:See it working here.