std::forward 和复制构造函数
std::forward and copy constructor
最近开始学习C++。我对 std::forward 有左值引用和右值引用的问题。
据我了解,以下代码中的 Func(mc) 假设调用
Func(T& t) 因为模板参数推导规则。并且应该在函数内部调用 MyClass 的复制构造函数,并带有一条消息 "copy constructor"。但是当我 运行 程序时我无法得到它。
我检查了带有右值引用和复制构造函数的 std::forwad 在其他行中是否运行良好。
请帮助我理解代码中发生的事情。如果我容易犯错误或误解,很抱歉占用您的时间。非常感谢。
class MyClass {
public:
MyClass() { printf("constructor.\n"); };
MyClass(MyClass&) { printf("copy constructor.\n"); };
MyClass(const MyClass&) { printf("const copy constructor.\n"); };
MyClass(MyClass&&) { printf("move constructor.\n"); };
int val = 3;
};
template <typename T>
void Func(T&& t) {
T new_t_(std::forward<T>(t));
new_t_.val *= 2;
};
main() {
MyClass mc;
Func(mc); // lvalue <- Func(T&)
Func(MyClass()); // rsvalue <- Func(T&&)
printf("mc.val=%d\n", mc.val); // this is for check
MyClass mc2(mc); // this is for check of copy constructor
}
当我运行程序跟随时的输出,
constructor.
constructor.
move constructor.
mc.val=6
copy constructor.
我认为第一条和第二条 "constructor" 消息之间应该有 "copy constructor"。
再次感谢您。
Func(mc);
此调用会将 T
推断为 MyClass&
。注意引用。这是由于 C++ 中引入了完美转发的方式:一个 转发引用 例如 Func
的 T&&
函数参数将被推断为左值如果相应的函数参数是左值表达式,则引用;否则(对于 xvalues 和 prvalues)它不会被推断为引用类型。
然后,为 T
推导的类型将与函数参数 T&& t
中的 &&
进行引用折叠,以形成最终的函数参数类型。在Func(mc)
、T == MyClass&
的情况下,函数参数变为MyClass& &&
,折叠为MyClass&
。
因为T == MyClass&
,局部变量new_t_
的声明在这种情况下会声明一个引用。不会创建新对象:
template <>
void Func<MyClass&>(MyClass& t) {
MyClass& new_t_(std::forward<MyClass&>(t));
new_t_.val *= 2;
}
Func(MyClass());
这里,函数参数是纯右值表达式MyClass()
。 T
将推导为 MyClass
(无引用)。生成的函数模板实例化如下所示:
template <>
void Func<MyClass>(MyClass&& t) {
MyClass new_t_(std::forward<MyClass>(t));
new_t_.val *= 2;
}
如果您想 copy/move 将函数参数转换为局部变量,可以使用 std::decay
元函数 - 或者,更具体地针对 OP 的问题,std::remove_reference
元函数。例如
template <typename T>
void Func(T&& t) {
using DT = typename std::decay<T>::type;
DT new_t_(std::forward<T>(t));
new_t_.val *= 2;
}
最近开始学习C++。我对 std::forward 有左值引用和右值引用的问题。 据我了解,以下代码中的 Func(mc) 假设调用 Func(T& t) 因为模板参数推导规则。并且应该在函数内部调用 MyClass 的复制构造函数,并带有一条消息 "copy constructor"。但是当我 运行 程序时我无法得到它。 我检查了带有右值引用和复制构造函数的 std::forwad 在其他行中是否运行良好。 请帮助我理解代码中发生的事情。如果我容易犯错误或误解,很抱歉占用您的时间。非常感谢。
class MyClass {
public:
MyClass() { printf("constructor.\n"); };
MyClass(MyClass&) { printf("copy constructor.\n"); };
MyClass(const MyClass&) { printf("const copy constructor.\n"); };
MyClass(MyClass&&) { printf("move constructor.\n"); };
int val = 3;
};
template <typename T>
void Func(T&& t) {
T new_t_(std::forward<T>(t));
new_t_.val *= 2;
};
main() {
MyClass mc;
Func(mc); // lvalue <- Func(T&)
Func(MyClass()); // rsvalue <- Func(T&&)
printf("mc.val=%d\n", mc.val); // this is for check
MyClass mc2(mc); // this is for check of copy constructor
}
当我运行程序跟随时的输出,
constructor.
constructor.
move constructor.
mc.val=6
copy constructor.
我认为第一条和第二条 "constructor" 消息之间应该有 "copy constructor"。
再次感谢您。
Func(mc);
此调用会将 T
推断为 MyClass&
。注意引用。这是由于 C++ 中引入了完美转发的方式:一个 转发引用 例如 Func
的 T&&
函数参数将被推断为左值如果相应的函数参数是左值表达式,则引用;否则(对于 xvalues 和 prvalues)它不会被推断为引用类型。
然后,为 T
推导的类型将与函数参数 T&& t
中的 &&
进行引用折叠,以形成最终的函数参数类型。在Func(mc)
、T == MyClass&
的情况下,函数参数变为MyClass& &&
,折叠为MyClass&
。
因为T == MyClass&
,局部变量new_t_
的声明在这种情况下会声明一个引用。不会创建新对象:
template <>
void Func<MyClass&>(MyClass& t) {
MyClass& new_t_(std::forward<MyClass&>(t));
new_t_.val *= 2;
}
Func(MyClass());
这里,函数参数是纯右值表达式MyClass()
。 T
将推导为 MyClass
(无引用)。生成的函数模板实例化如下所示:
template <>
void Func<MyClass>(MyClass&& t) {
MyClass new_t_(std::forward<MyClass>(t));
new_t_.val *= 2;
}
如果您想 copy/move 将函数参数转换为局部变量,可以使用 std::decay
元函数 - 或者,更具体地针对 OP 的问题,std::remove_reference
元函数。例如
template <typename T>
void Func(T&& t) {
using DT = typename std::decay<T>::type;
DT new_t_(std::forward<T>(t));
new_t_.val *= 2;
}