This question is a follow up to this one: Explicit template specialization for templated constructor of templated class The answers given in the other question are of course
1) Can you explain why the program does not behave as I expected?
On this line:
var
i1(i0); // expect 'copy' -> get 'general rvalue'
The var(T&&)
constructor is instantiated with T
substituted with var
, i.e. producing a constructor with this signature:
var(var&);
That constructor is a better match than the implicit copy constructor var(const var&)
because i0
is non-const.
Similarly for:
var
i3(s); // expect 'general lvalue' -> get 'general rvalue'
s
is non-const, so the var(T&&)
constructor is instantiated with T
substituted with std::string&
, producing a constructor with the signature:
var(std::string&);
For a non-const argument that constructor is a better match than the other constructor template, which produces:
var(const std::string&);
You need to realise that the var(T&&)
constructor is not a "general rvalue" constructor, because T&&
can match any type including lvalues.
See Universal References in C++11 for more details.
2) How can I make the program to call the copy and move constructor of var when it gets a var and the templated constructors otherwise?
Constrain the templates so they don't accept any type.
template
using Is_a_var = std::is_same::type, var>;
template
using Enable_if_not_a_var = typename std::enable_if::value>::type;
template>
var(T&& t) {
std::cout << "general value" << std::endl;
}
I would also add defaulted copy/move constructors, to be clear to readers you want them:
var(const var&) = default;
var(var&&) = default;
3) And finally, I'm trying to put the two templated constructors into one handling both lvalues and rvalues and forwarding them to another function using std::forward - how could this look like?
Don't. The var(T&&)
constructor already accepts both rvalues and lvalues.
Use std:forward
to forward the argument to other functions:
template>
var(T&& t) : m_something(std::forward(t)) {
std::cout << "general value" << std::endl;
}