lundi 20 août 2018

class constructor precedence with a variadic template constructor for a value wrapper

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

But if I make w0 const

wrapper<std::string> w0 const {s1}; // value copy constructor  
// wrapper<std::string> w3{w0};     // compilation error

I get the same error.

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?

Aucun commentaire:

Enregistrer un commentaire