Today I've discovered that I don't understand the C++ constructor precedence rules.
Please, see the following template struct wrapper
template <typename T>
struct wrapper
{
T value;
wrapper (T const & v0) : value{v0}
{ std::cout << "value copy constructor" << std::endl; }
wrapper (T && v0) : value{std::move(v0)}
{ std::cout << "value move constructor" << std::endl; }
template <typename ... As>
wrapper (As && ... as) : value(std::forward<As>(as)...)
{ std::cout << "emplace constructor" << std::endl; }
wrapper (wrapper const & w0) : value{w0.value}
{ std::cout << "copy constructor" << std::endl; }
wrapper (wrapper && w0) : value{std::move(w0.value)}
{ std::cout << "move constructor" << std::endl; }
};
It's a simple template value wrapper with copy constructor (wrapper const &
), a move constructor (wrapper && w0
), a sort of value copy constructor (T const & v0
), a sort of move constructor (T && v0
) and a sort of template construct-in-place-the-value constructor (As && ... as
, following the example of emplace
methods for STL containers).
My intention was to use the copy or moving constructor calling with a wrapper, the value copy or move constructor passing a T
object and the template emplace constructor calling with a list of values able to construct an object of type T
.
But I don't obtain what I expected.
From the following code
std::string s0 {"a"};
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w1{std::move(s0)}; // value move constructor
wrapper<std::string> w2{1u, 'b'}; // emplace constructor
//wrapper<std::string> w3{w0}; // compilation error (?)
wrapper<std::string> w4{std::move(w0)}; // move constructor
The w1
, w2
and w4
values are constructed with value move constructor, emplace constructor and move constructor (respectively) as expected.
But w0
is constructed with emplace constructor (I was expecting value copy constructor) and w3
isn't constructed at all (compilation error) because the emplace constructor is preferred but ins't a std::string
constructor that accept a wrapper<std::string>
value.
First question: what am I doing wrong?
I suppose that the w0
problem it's because s0
isn't a const
value, so the T const &
isn't an exact match.
Indeed, if I write
std::string const s1 {"a"};
wrapper<std::string> w0{s1};
I get the value copy constructor called
Second question: what I have to do to obtain what I want?
So what I have to do to make the value copy constructor (T const &
) to get the precedence over the emplace constructor (As && ...
) also with not constant T
values and, mostly, what I have to do to get the copy constructor (wrapper const &
) take the precedence constructing w3
?