类似函数的化简函数中的转发和返回类型
Forwards and return type(s) in functional-like reduce function
我需要创建一个类似于std::reduce
的reduce
函数,但这个函数应该处理可变参数,而不是处理容器。
这是我目前拥有的:
template <typename F, typename T>
constexpr decltype(auto) reduce(F&&, T &&t) {
return std::forward<T>(t);
}
template <typename F, typename T1, typename T2, typename... Args>
constexpr decltype(auto) reduce(F&& f, T1&& t1, T2&& t2, Args&&... args) {
return reduce(
std::forward<F>(f),
std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)),
std::forward<Args>(args)...);
}
以下工作按预期工作:
std::vector<int> vec;
decltype(auto) u = reduce([](auto &a, auto b) -> auto& {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
}, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6});
assert(&vec == &u); // ok
assert(vec == std::vector<int>{1, 2, 3, 4, 5, 6}); // ok
但以下方法不起作用:
auto u = reduce([](auto a, auto b) {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
}, std::vector<int>{}, std::set<int>{1, 2},
std::list<int>{3, 4}, std::vector<int>{5, 6});
这基本上崩溃了 - 为了完成这项工作,我需要将reduce
的第一个定义更改为:
template <typename F, typename T>
constexpr auto reduce(F&&, T &&t) {
return t;
}
但是如果我这样做,第一个片段就不再起作用了。
问题在于reduce
函数的参数和返回类型的转发,但我可以找到它。
我应该如何修改我的reduce
定义以使两个代码段都正常工作?
你可以试试
template <typename F, typename T>
constexpr T reduce(F&&, T &&t) {
return std::forward<T>(t);
}
当第二个参数是右值时,这将返回一个 prvalue,否则返回一个引用参数的左值。你的片段似乎很好。
或者,只需使用您的第二个变体并vec
包装在std::ref
中,比照使用。这也是模板按值处理对象的标准方法。
问题案例中的 lambda:
[](auto a, auto b) {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
}
按值返回,因此当reduce
递归时:
return reduce(
std::forward<F>(f),
std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)), // HERE
std::forward<Args>(args)...);
第二个参数是从该按值返回对象临时初始化的参数。当递归最终终止时:
template <typename F, typename T>
constexpr decltype(auto) reduce(F&&, T &&t) {
return std::forward<T>(t);
}
它返回绑定到该临时对象的引用,该引用在展开递归时被销毁,以便从悬空引用初始化v
。
最简单的解决方法是不要在lambda中创建临时,而是将结果累积在输入对象中,该输入对象至少会持续到完整表达式(DEMO(结束:
auto fn = [](auto&& a, auto const& b) -> decltype(auto) {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
// Or better:
// a.insert(std::end(a), std::begin(b), std::end(b));
return static_cast<decltype(a)>(a);
};
std::vector<int> vec;
decltype(auto) u = reduce(fn, vec,
std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6});
assert(&vec == &u); // ok
assert((vec == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok
auto v = reduce(fn, std::vector<int>{},
std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6});
assert((v == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok
有人提到了折叠表达式。
template<class F, class T=void>
struct reduce_t;
template<class F>
reduce_t<F> reduce( F&& f );
template<class F, class T>
reduce_t<F, T> reduce( F&& f, T&& t );
template<class F, class T>
struct reduce_t {
F f;
T t;
template<class Rhs>
auto operator|( Rhs&& rhs )&&{
return reduce( f, f( std::forward<T>(t), std::forward<Rhs>(rhs) ) );
}
T get()&&{ return std::forward<T>(t); }
};
template<class F>
struct reduce_t<F,void> {
F f;
template<class Rhs>
auto operator|( Rhs&& rhs )&&{
return reduce( f, std::forward<Rhs>(rhs) );
}
};
template<class F>
reduce_t<F> reduce( F&& f ) {
return {std::forward<F>(f)};
}
template<class F, class T>
reduce_t<F, T> reduce( F&& f, T&& t ) {
return {std::forward<F>(f), std::forward<T>(t)};
}
template<class F, class T, class...Ts>
auto reduce( F&& f, T&& t, Ts&&...ts ) {
return (reduce( std::forward<F>(f), std::forward<T>(t) ) | ... | std::forward<Ts>(ts));
}
然后这些中的任何一个都可以工作:
decltype(auto) u = (reduce([](auto &a, auto b) -> auto& {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
}) | vec | std::set<int>{1, 2} | std::list<int>{3, 4} | std::vector<int>{5, 6}).get();
decltype(auto) u = reduce([](auto &a, auto b) -> auto& {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
}, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}).get();
auto u_val = (
reduce([](auto a, auto b) {
std::copy(std::begin(b), std::end(b), std::back_inserter(a));
return a;
})
| std::vector<int>{} | std::set<int>{1, 2}
| std::list<int>{3, 4} | std::vector<int>{5, 6}
).get();
活生生的例子。
相关文章:
- 将函数参数完美转发到函数指针:按值传递呢?
- 在按值调用 (c++) 中转发构造函数参数
- 使用函数指针转发声明作为 lamba 声明
- 完美的转发和构造函数
- 我可以在构造函数的主体中转发构造吗?
- 通过类型别名从构造函数转发模板推导
- 在可变参数函数中转发特定范围的参数
- 为什么添加析构函数(甚至是空的)会破坏我的结构,该结构使用 ref 转发和折叠来保存 ref 或值的副本?
- 如何在程序集函数中将元素数组作为参数传递时转发 ARM 寄存器的地址指针
- 如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?
- 类似函数的化简函数中的转发和返回类型
- 在编写包装现有函数并检查错误的模板函数时,如何使用完美转发?
- 将可变参数函数参数转发到 std::function 对象
- 将转发 lambda 转换为函数指针
- 如何构造一个 std::variant 类型对象,其自身 Templated 和构造函数转发参数
- C++中的友元成员函数 - 转发声明不起作用
- 将构造函数转发到成员对象
- 通过函数转发常量引用
- 函数转发参数而不执行任何操作
- 将参数从可变参数函数转发到固定参数函数