这个问题是对此问题的跟进:Explicit template specialization for templated constructor of templated class 在另一个问题中给出的答案当然是正确的但事实证明我并不是在问我想问的问题 - 所以这是一个新问题:
请考虑以下代码:
template<typename First, typename ... Rest> class var {
public:
var() {
std::cout << "default" << std::endl;
}
var(const var& v) {
std::cout << "copy" << std::endl;
}
var(var&& v) {
std::cout << "move" << std::endl;
}
template<typename T>
var(const T& t) {
std::cout << "general lvalue" << std::endl;
}
template<typename T>
var(T&& t) {
std::cout << "general rvalue" << std::endl;
}
};
int main()
{
var<int> i0; // expect 'default' -> get 'default'
var<int> i1(i0); // expect 'copy' -> get 'general rvalue'
var<int> i2(std::move(i0)); // expect 'move' -> get 'move'
std::string s("Hello");
var<int> i3(s); // expect 'general lvalue' -> get 'general rvalue'
var<int> i4(std::move(s)); // expect 'general rvalue' -> get 'general rvalue'
}
我在main函数中写了哪些构造函数我希望被调用,哪些实际上被调用。以下是我的问题:
1)你能解释为什么程序没有按照我的预期行事吗?
2)如何使程序调用副本并在获取var时移动var的构造函数,否则会移动模板化的构造函数?
3)最后,我试图将两个模板化的构造函数放在一个处理左值和右值并使用std :: forward将它们转发到另一个函数 - 这看起来怎么样?
答案 0 :(得分:3)
1)你能解释为什么程序没有按照我的预期行事吗?
在这一行:
var<int> i1(i0); // expect 'copy' -> get 'general rvalue'
var(T&&)
构造函数的实例化T
替换为var<int>&
,即生成带有此签名的构造函数:
var(var&);
该构造函数比隐式复制构造函数var(const var&)
更好匹配,因为i0
是非const。
同样适用于:
var<int> i3(s); // expect 'general lvalue' -> get 'general rvalue'
s
是非const的,因此var(T&&)
构造函数实例化为T
替换为std::string&
,从而生成带有签名的构造函数:
var(std::string&);
对于非const参数,构造函数与其他构造函数模板匹配得更好,后者产生:
var(const std::string&);
您需要意识到var(T&&)
构造函数不是“一般rvalue”构造函数,因为T&&
可以匹配任何类型的,包括lvalues 。
有关详细信息,请参阅Universal References in C++11。
2)如何让程序在获取var时调用副本并移动var的构造函数,否则移动构造函数?
限制模板,使其不接受任何类型。
template<typename T>
using Is_a_var = std::is_same<typename std::decay<T>::type, var>;
template<typename T>
using Enable_if_not_a_var = typename std::enable_if<!Is_a_var<T>::value>::type;
template<typename T, typename Constraint = Enable_if_not_a_var<T>>
var(T&& t) {
std::cout << "general value" << std::endl;
}
我还会添加默认的复制/移动构造函数,以便读者明白你想要它们:
var(const var&) = default;
var(var&&) = default;
3)最后,我试图将两个模板化的构造函数放在一个处理左值和右值并使用std :: forward将它们转发到另一个函数 - 这看起来怎么样?
别。 var(T&&)
构造函数已接受rvalues 和 lvalues。
使用std:forward<T>(t)
将参数转发给其他函数:
template<typename T, typename Constraint = Enable_if_not_a_var<T>>
var(T&& t) : m_something(std::forward<T>(t)) {
std::cout << "general value" << std::endl;
}