此解决方案不会引入不必要的副本,也不会出现某些 cmets 建议的错误转发。解释如下。
您可以使用一些具有开始和结束功能的包装器
返回反向迭代器。
template<class T>
struct revert_wrapper
{
T o;
revert_wrapper(T&& i) : o(std::forward<T>(i)) {}
};
template<class T>
auto begin(revert_wrapper<T>& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T>& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto begin(revert_wrapper<T> const& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T> const& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto reverse(T&& ob)
{
return revert_wrapper<T>{ std::forward<T>(ob) };
}
这样使用:
std::vector<int> v{1, 2, 3, 4};
for (auto i : reverse(v))
{
std::cout << i << "\n";
}
或者在你的情况下
for ( auto& i : reverse(lifo_stack) ) {
cout << "Current loop iteration has i = " << i << endl;
cout << "Popped an item from the stack: " << lifo_stack.pop() << endl;
}
由于转发不是一个简单的话题,并且存在误解,我将进一步解释一些细节。我将使用std::vector<int> 作为“待反转”类型T 的示例。
1。函数模板reverse。
1.1 传递左值std::vector<int>:
std::vector<int> v{1, 2, 3, 4};
auto&& x = reverse(v);
在这种情况下,编译器创建的 reverse 实例如下所示:
template<>
auto reverse<std::vector<int>&>(std::vector<int>& ob)
{
return revert_wrapper<std::vector<int>&>{ std::forward<std::vector<int>&>(ob) };
}
我们在这里看到两件事:
-
revert_wrapper 的 T 将是 std::vector<int>&,因此不涉及复制。
- 我们将左值作为左值转发给
revert_wrapper 的构造函数
1.2 传递右值std::vector<int>
std::vector<int> foo();
auto&& x = reverse(foo());
我们再看函数模板的实例化:
template<>
auto reverse<std::vector<int>>(std::vector<int>&& ob)
{
return revert_wrapper<std::vector<int>>{ std::forward<std::vector<int>>(ob) };
}
并且可以再次注意两件事:
-
revert_wrapper 的 T 将是 std::vector<int>,因此复制向量,防止右值在任何基于范围的循环运行之前超出范围
- 右值
std::vector<int>&& 将被转发给revert_wrapper 的构造函数
2。类模板revert_wrapper 及其构造函数
2.1 reverse 在左值std::vector<int>& 的情况下创建的revert_wrapper
template<>
struct revert_wrapper<std::vector<int>&>
{
std::vector<int>& o;
revert_wrapper(std::vector<int>& i) :
o(std::forward<std::vector<int>&>(i)) {}
};
如上所述:我们存储参考时不涉及副本。
forward 看起来也很熟悉,实际上它与上面的reverse 相同:我们将左值作为左值引用。
2.2 reverse 在右值std::vector<int>&& 的情况下创建的revert_wrapper
template<>
struct revert_wrapper<std::vector<int>>
{
std::vector<int> o;
revert_wrapper(std::vector<int>&& i) :
o(std::forward<std::vector<int>>(i)) {}
};
这一次我们将对象按值存储以防止悬空引用。
转发也很好:我们将右值引用从reverse 转发到revert_wrapper 构造函数,然后我们将它转发到std::vector 构造函数。我们可以以同样的方式使用static_cast<T&&>(i),但我们不是来自左值的(std::)mov(e),而是转发:
我们还可以在这里看到另一件事:
按值存储的revert_wrapper 实例的唯一可用构造函数采用右值。因此,我们不能(轻易)欺骗这个类来制作不必要的副本。
请注意,在 revert_wrapper 构造函数的 o 的初始化程序中将 std::forward 替换为 std::move 实际上是错误的。