移动语义完美转发
移动语义完美转发
测试代码和输出
#include <iostream>
template <typename T> void judge(const T &&t) {
std::cout << "&&:\t" << t << std::endl;
}
template <typename T> void judge(const T &t) {
std::cout << "&:\t" << t << std::endl;
}
template <typename T> void f(T &&t) {
judge(std::forward<T>(t));
judge(t);
judge(std::move(t));
}
int main() {
int x = 10;
f(x);
std::cout << std::endl;
f(11);
return 0;
}
output:
&: 10
&: 10
&&: 10
&&: 11
&: 11
&&: 11
可以看到 std::forward 能完美转发,什么都不带的话是左值,而 std::move 永远是一个 rvalue。
分析
首先老生常谈,在 f 当中,参数 t 一定是一个 lvalue。但在所谓 universal reference 下有不同意义 :
f(11)中,t是一个指向rvalue的lvaluef(x)中,t是一个指向lvalue的lvalue
关于 universal reference
这是 Scott Meyers 创造的术语。看下例:
int &&z1 = x; // Error
auto &&z2 = x; // z2: int &
1: 行处报错:
Rvalue reference to type 'int' cannot bind to lvalue of type 'int' clang(lvalue_to_rvalue_ref)
2: 行处正确,但编译器显示 z2: int &,也就是说 z2 在这里是个 lvalue reference
注意:univeral reference出现的前提时在推演的时候,原文:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
个人不太理解为什么要把模板推导引用和右值引用以相同符号表示,虽然确实说得通,但初见很容易误导:
wow, great answer! All seems clearer now. I must say I'm new to learning C++ by myself, so there are a bit new unfamiliar terms to me in your discussion, specifically, what does the double ampersand '&&' mean right after 'T'? I thought thats only used for the logical AND.
其中 std::move
definition: ( from the GNU ISO C++ Library )
/**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{
return static_cast<typename std::remove_reference<_Tp>::type&&>(__t);
}
其中 std::forward
definition: ( from the GNU ISO C++ Library )
/**
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{
return static_cast<_Tp&&>(__t);
}
/**
* @brief Forward an rvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
// this assert seems redundant, I'm not sure
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
关于: 引用模板参数推导
据称 c++ 编译器将自动推导到一个
- 合法的 legal
- 最少推导的 with the least amount of deduction
类型
首先稍微测试一下( type_name() 抄自此回答)。
template <typename T> void g0(T t) { //
std::cout << "type of T: " << type_name<T>()
<< ", decltype(t): " << type_name<decltype(t)>() << std::endl;
}
template <typename T> void g1(T &t) {
std::cout << "type of T: " << type_name<T>()
<< ", decltype(t): " << type_name<decltype(t)>() << std::endl;
}
template <typename T> void g2(T &&t) {
std::cout << "type of T: " << type_name<T>()
<< ", decltype(t): " << type_name<decltype(t)>() << std::endl;
}
int main() {
int x = 10;
int &y = x;
int &&z = 12;
std::cout << std::endl;
std::cout << type_name<int>() << std::endl
<< type_name<int &>() << std::endl
<< type_name<int &&>() << std::endl
<< std::endl;
g0(1);
g0(x);
g0(y);
g0(z);
g0(std::move(x));
std::cout << std::endl;
// g1(1); Error: no match function
g1(x);
g1(y);
g1(z);
// g1(std::move(x)); Error: no match function
std::cout << std::endl;
g2(1);
g2(x);
g2(y);
g2(z);
g2(std::move(x));
return 0;
}
output:
int
int &
int &&
type of T: int, decltype(t): int
type of T: int, decltype(t): int
type of T: int, decltype(t): int
type of T: int, decltype(t): int
type of T: int, decltype(t): int
type of T: int, decltype(t): int &
type of T: int, decltype(t): int &
type of T: int, decltype(t): int &
type of T: int, decltype(t): int &&
type of T: int &, decltype(t): int &
type of T: int &, decltype(t): int &
type of T: int &, decltype(t): int &
type of T: int, decltype(t): int &&
没有一个 T 推导成了 int &&。
与我原本猜想不符的地方包括
g0(int &)中,T 会不会被推导为int &g0(int &&)中,T 会不会被推导为int &&g2(int &&)中,T 会不会被推导为int &&以便引用折叠
说实话,这 T 的推导结果疑似被编译器优化过。不过总的来说还是很合理的
关于: std::forward<T> 的模板参数
开了 -std=c++17 也不能自动推导,怎么会事呢
关于: && & && 引用折叠
编译器禁止你写 & && 这样的代码(声明到引用的引用,这没有意义),因而这种写法就被编译器拿来表示另一种含义,即引用折叠( Reference collapsing )
Reference collapsing
It is permitted to form references to references through type manipulations in templates or typedefs, in which case the reference collapsing rules apply: rvalue reference to rvalue reference collapses to rvalue reference, all other combinations form lvalue reference:
注意:与 universal reference 一样,这同样只发生在类型操作中。
(说是类型操作,实际上还是需要在类型推导中。下面这样的代码仍然会报错:)
typedef int &&&&&B; // 'Tp' declared as a reference to a reference
// *clang(illegal_decl_reference_to_reference)*
using B = int &&&&&;// Type name declared as a reference to a reference
// *clang(illegal_decl_reference_to_reference)*
所以你大概只能像这样去手动操作引用折叠
template <typename T> struct A1 { using Type = T &; };
template <typename T> struct A2 { using Type = T &&; };
int main() {
int x = 10;
A1<A1<int>::Type>::Type y = x; // Right
A2<A2<int>::Type>::Type z = 12; // Right
A1<A2<int>::Type>::Type zz = x; // Right
// int &&&zz = x; Error: 'zz' declared as a reference to a
// reference *clang(illegal_decl_reference_to_reference)*
return 0;
}
关于: std::remove_reference
definition: ( from the GNU ISO C++ Library )
/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
根据前面的测试,T 可能被推导为 lvalue reference,因而需要 remove 一下
关于: 参数的 const 修饰符
template <typename T> void w0(T &&t) { //
std::cout << t << std::endl;
}
template <typename T> void w1(const T &&t) { //
std::cout << t << std::endl;
}
int main() {
auto &&_x = 12;
auto &&x = _x;
int y = 13;
w0(std::forward<int>(_x));
w0(std::forward<int>(x));
w0(y);
w1(std::forward<int>(_x));
w1(std::forward<int>(x));
// w1(y); // Candidate function [with T = int] not viable: expects an rvalue
// for 1st argument
return 0;
}
w1 处的 const 使得 && 不再是 universal reference,而是 rvalue reference,见此问答

浙公网安备 33010602011771号