dod*_*ndi 2 c++ for-loop generic-programming c++17 c++20
想象一下,你有这个通用的伪代码:
template<typename Iterable>
void f(Iterable&& iterable)
{
...
}
Run Code Online (Sandbox Code Playgroud)
我们想要处理对可迭代对象1的 rvalue和lvalue引用,并且想法是该函数处理容器逐元素地执行操作.
我们想要将容器的参考规范转发给元素是合理的.换句话说,如果iterable是右值引用,则该函数必须从容器中移动元素.
使用C++ 17,我会这样做
auto [begin, end] = [&] {
if constexpr(std::is_lvalue_reference_v<Iterable>)
return std::array{std::begin(iterable),
std::end(iterable)};
else
return std::array{
std::make_move_iterator(std::begin(iterable)),
std::make_move_iterator(std::end(iterable))};
}();
std::for_each(begin, end, [&](auto&& element)
{
...
});
Run Code Online (Sandbox Code Playgroud)
显然,这不是维护2的最佳代码,容易出错,并且编译器可能不那么容易优化.
我的问题是:对于未来的C++标准,有可能引入转发基于范围的循环的概念吗?如果这样会好的
for(auto&& el : std::move(iterable))
{
...
}
Run Code Online (Sandbox Code Playgroud)
可以处理el作为右值参考.通过这种方式,这将是可能的:
template<typename Iterable>
void f(Iterable&& iterable)
{
for(auto&& el : std::forward<Iterable>(iterable))
{
/*
* el is forwarded as lvalue reference if Iterable is lvalue reference,
* as rvalue reference if Iterable is rvalue reference
*/
external_fun(std::forward<decltype(el)>(el));
}
}
Run Code Online (Sandbox Code Playgroud)
我担心代码破坏性的变化,但与此同时,我无法考虑将rvalue引用作为基于范围的循环的参数传递而不需要移动对象的情况.
正如所建议的那样,我试着写下如何更改标准的6.5.4部分.草案可以在这个地址阅读.
您是否认为可以在不引入严重问题的情况下引入此功能?
1使用C++ 20概念或static_asserts进行检查
2如果没有C++ 17,情况会更糟
这不行.从根本上说,你可以迭代两种东西:那些拥有元素的东西,以及那些没有元素的东西.对于非拥有范围,范围的值类别并不重要.他们没有自己的元素,所以你不能安全地离开他们.基于范围的for循环必须适用于这两种范围.
还有一些需要考虑的极端情况(例如,代理迭代器).基于范围的for循环基本上是语法糖,它对被迭代的事物只强加了一组非常小的要求.好处是它可以迭代很多东西.成本是它没有太多聪明的空间.
如果你知道iterable实际上拥有它的元素(这样移动是安全的),那么你需要的只是一个根据其他东西的值类别转发东西的函数:
namespace detail {
template<class T, class U>
using forwarded_type = std::conditional_t<std::is_lvalue_reference<T>::value,
std::remove_reference_t<U>&,
std::remove_reference_t<U>&&>;
}
template<class T, class U>
detail::forwarded_type<T,U> forward_like(U&& u) {
return std::forward<detail::forwarded_type<T,U>>(std::forward<U>(u));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
887 次 |
| 最近记录: |