C++ 为什么要使用完全转发的值(函子)?
C++11(和C++14)引入了针对泛型编程的其他语言构造和改进。这些特点包括:C++ 为什么要使用完全转发的值(函子)?,c++,c++11,perfect-forwarding,c++14,C++,C++11,Perfect Forwarding,C++14,C++11(和C++14)引入了针对泛型编程的其他语言构造和改进。这些特点包括: R值参考 参考折叠 完美转发 移动语义、可变模板等 我浏览了一个早期版本(现在有更新的文本)和§20.5.1编译时整数序列示例中的代码,我发现这些代码既有趣又奇特 template<class F, class Tuple, std::size_t... I> decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequ
- R值参考
- 参考折叠
- 完美转发
- 移动语义、可变模板等
template<class F, class Tuple, std::size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices = make_index_sequence<std::tuple_size<Tuple>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}
模板
decltype(自动)apply\u impl(F&&F,元组&&t,索引\u序列){
返回std::forward(f)(std::get(std::forward(t))…);
}
模板
decltype(自动)应用(F&&F、元组&&t){
使用索引=生成索引序列;
返回apply_impl(std::forward(f)、std::forward(t)、index());
}
在线这里
问题
- 为什么转发
中的函数apply\u impl
,即为什么转发f
std::forward(f)(std::get…
- 为什么不将函数作为
)应用呢f(std::get…
struct Functor1 {
int operator()(int id) const
{
std::cout << "Functor1 ... " << id << std::endl;
return id;
}
};
输出与预期一致,与是否使用r值无关或l值func
在调用apply\u impl
和apply\u impl\u 2
时,会调用重载的调用运算符。它同时对r值和l值进行调用。在C++03下,这就是您所能得到的,您不能基于对象的“r值属性”或“l值属性”重载成员方法
函数1…1函数1…2
函数1…3
函子1…4 参考合格样品 我们现在需要让呼叫接线员超负荷工作,以便进一步扩展
struct Functor2 {
int operator()(int id) const &
{
std::cout << "Functor2 &... " << id << std::endl;
return id;
}
int operator()(int id) &&
{
std::cout << "Functor2 &&... " << id << std::endl;
return id;
}
};
输出为
Functor2&…5Functor2&…6
Functor2&…7
函子2&…8 讨论 在
apply\u impl\u 2
(id
5和6)的情况下,输出与最初预期的不一样。在这两种情况下,调用l值限定的运算符()
(根本不调用r值)。从Functor2()开始,可能预期
是一个r值,用于调用apply\u impl\u 2
将调用的r值限定的运算符()
。func
作为apply\u impl\u 2
的命名参数,是一个r值引用,但由于它被命名,它本身就是一个l值。因此,l值限定的运算符()(int)const&
在l-valuefunc2
作为参数和r-valueFunctor2()
作为参数的情况下都被调用
在apply_impl
(id
7和8)的情况下,std::forward(func)
维护或保留为func
提供的参数的r值/l值性质。因此,l值限定为运算符()(int)当r值Functor2()
用作参数时,调用const&
,l值func2
用作参数,r值限定运算符()(int)&
用作参数。这种行为是预期的
结论
通过完美转发使用std::forward
,确保我们保留了func
原始参数的r值/l值性质。它保留了它们的属性。
它是必需的,std::forward
不仅可以而且应该用于将参数转发给函数,而且在必须保留r-value/l-value性质的情况下需要使用参数时也可以使用。注意:在这些情况下,r-value/l-value不能或不应该被保留不应使用(见下文相反)
出现了许多例子,它们通过看似无辜地使用r值引用,无意中失去了参数的r值/l值性质
编写定义良好且合理的通用代码一直很困难。随着r值引用的引入,尤其是引用崩溃,编写更好的通用代码变得可能,更简洁,但我们需要更加了解所提供参数的原始性质,并确保它们是当我们在我们编写的通用代码中使用它们时,会保留它们
可以找到完整的示例代码
推论与逆
- 这个问题的一个推论是:假设引用在模板化函数中崩溃,参数的r值/l值性质是如何保持的?答案是-use
std::forward(t)
- Converse;
是否解决了所有“通用参考”问题?不,它不会解决,在某些情况下,它会解决问题,例如多次转发值std::forward
完美转发的简要背景 完美转发对某些人来说可能并不熟悉,那么什么是完美转发呢 简言之,完美转发是为了确保提供给函数的参数被转发(传递)给另一个具有相同值类别(基本上是r值与l值)的函数。它通常与可能发生的模板函数一起使用 Scott Meyers在其著作中给出了以下伪代码,以解释
std::forward
(大约在20分钟时)的工作原理
模板
T&前进
struct Functor1 {
int operator()(int id) const
{
std::cout << "Functor1 ... " << id << std::endl;
return id;
}
};
int main()
{
Functor1 func1;
apply_impl_2(func1, 1);
apply_impl_2(Functor1(), 2);
apply_impl(func1, 3);
apply_impl(Functor1(), 4);
}
struct Functor2 {
int operator()(int id) const &
{
std::cout << "Functor2 &... " << id << std::endl;
return id;
}
int operator()(int id) &&
{
std::cout << "Functor2 &&... " << id << std::endl;
return id;
}
};
int main()
{
Functor2 func2;
apply_impl_2(func2, 5);
apply_impl_2(Functor2(), 6);
apply_impl(func2, 7);
apply_impl(Functor2(), 8);
}
template <typename T>
T&& forward(T&& param) { // T&& here is formulated to disallow type deduction
if (is_lvalue_reference<T>::value) {
return param; // return type T&& collapses to T& in this case
}
else {
return move(param);
}
}
struct concat {
std::vector<int> state;
std::vector<int> const& operator()(int x)&{
state.push_back(x);
return state;
}
std::vector<int> operator()(int x)&&{
state.push_back(x);
return std::move(state);
}
std::vector<int> const& operator()()&{ return state; }
std::vector<int> operator()()&&{ return std::move(state); }
};
auto result = apply( concat{}, std::make_tuple(2) );