这篇文章是讲 c++ 完美转发的
前提:
c++ 的变量有值语义,所以为了避免调用大开销的复制构造函数,函数形参应该采用 const T& 或者 T&。
这里不讨论原始指针、智能指针,因为指针本身是传值。
如果需要在函数中修改形参的值,形参就只能用 T& 或者 T&&。所以 const T& 不在本篇文章讨论范围。
希望能够写包装函数,为已有函数添加功能或者写高阶函数。如:std::make_shared() std::tuple() std::function() ...
假设下面就是一个复制开销很大的类。
class Object {
public:
Object() {}
Object(const Object &obj)
{
printf("slow copy\n");
}
Object(Object &&obj)
{
printf("fast move\n");
}
};
还有对这个类进行处理的函数。
void solve(Object &&obj)
{
printf("solve rvalue\n");
}
void solve(Object &obj)
{
printf("solve lvalue\n");
}
以及测试用的 main。(wrap 函数后面定义)
int main()
{
printf("-- lvalue --\n");
Object obj;
wrap(obj);
printf("-- rvalue --\n");
wrap(Object());
printf("-- lvalue_reference --\n");
Object &obj2 = obj;
wrap(obj2);
printf("-- rvalue_reference --\n");
wrap(std::move(Object()));
return 0;
}
下面尝试给 solve(Object) 写个包装函数 wrap(Object)。
这个包装函数应该能够实现完美转发。即:
void wrap(T obj)
{
solve(obj);
}
输出:
-- lvalue --
slow copy
solve lvalue
-- rvalue --
solve lvalue
-- lvalue_reference --
slow copy
solve lvalue
-- rvalue_reference --
fast move
solve lvalue
形参类型 | T in wrap | type of obj |
---|---|---|
Object(左值) | Object | Object |
Object(右值) | Object | Object |
Object& | Object | Object |
Object&& | Object | Object |
template <typename T>
void wrap(T &obj)
{
solve(obj);
}
template <typename T>
void wrap(T &&obj)
{
solve(obj);
}
输出:
-- lvalue --
solve lvalue
-- rvalue --
solve lvalue
-- lvalue_reference --
solve lvalue
-- rvalue_reference --
solve lvalue
形参类型 | T in wrap | type of obj |
---|---|---|
Object(左值) | Object& | Object& |
Object(右值) | Object&& | Object&& |
Object& | Object& | Object& |
Object&& | Object&& | Object&& |
template <typename T>
void wrap(T &&obj)
{
solve(std::forward<T>(obj));
}
输出:
-- lvalue --
solve lvalue
-- rvalue --
solve rvalue
-- lvalue_reference --
solve lvalue
-- rvalue_reference --
solve rvalue
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) _NOEXCEPT
{
return static_cast<_Tp&&>(__t);
}
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) _NOEXCEPT
{
static_assert(!std::is_lvalue_reference<_Tp>::value,
"Can not forward an rvalue as an lvalue.");
return static_cast<_Tp&&>(__t);
}
形参类型 | _Tp | type of __t | static_cast<_Tp&&>(__t) |
---|---|---|---|
Object(左值) | Object | Object& | Object&&(右值) |
Object(右值) | Object | Object&& | Object&&(右值) |
Object& | Object& | Object& | Object&(左值) |
Object&& | Object&& | Object& | Object&&(右值) |
std::forward<int&>(100)
这是错误的写法。_Tp 必须是实参本身的类型。其实前面的都是废话。如果要实现完美转发,关键两点。
比如:
template <typename Func, typename... Param>
auto apply(Func f, Param&&... p) -> decltype(f(p...))
{
return f(std::forward<Param>(p)...);
}