在c++ 11中遍历堆栈时,无效的范围表达式

invalid range expression when looping through stack in c++ 11

本文关键字:无效 范围 表达式 堆栈 c++ 遍历      更新时间:2023-10-16

我试图在c++ 11中循环堆栈,然而,以下代码似乎无效,我可以以这种方式循环向量,为什么堆栈在这里不起作用?

#include <stack>
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
    stack<int> s;
    s.push(1);
    s.push(2);
    for (int val:s){
        cout<<val<<endl;
    }
    return 0;
}

std::stack不是容器(与std::vector不同)。它是一个容器适配器。它有意地将底层容器的功能限制为仅支持堆栈操作。它没有迭代器,没有beginend成员函数,这是range-for循环工作所必需的。堆栈不是用来迭代的。如果你想在集合上迭代,那么你就不需要堆栈。您需要一个序列容器,如std::vectorstd::deque

如果你想要的功能是某种序列容器和堆栈的混合体,(例如:支持迭代的东西,但只允许在一端插入和删除),然后您必须自己实现

因为stack没有iterators,也没有beginend。如果你需要用迭代器堆栈,你需要自己在其他容器(std::list, std::vector等)上实现它。

std::stack没有提供begin或end成员函数,因此它不能与基于范围的for循环一起工作,而这两者都需要,我们可以从cppreference的基于范围的for循环条目中看到,它扩展为:

{
 auto && __range = range_expression ;
 for (auto __begin = begin_expr,
            __end = end_expr;
        __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
 }
} 

:

如果__range的类型是具有begin或end成员函数的类类型,则begin_expr为__range.begin(), end_expr为__range.end();

或:

否则,begin_expr为begin(__range), end_expr为end(__range),它们是通过与std作为关联命名空间的参数相关查找找到的。

基于范围的循环产生的代码类似如下:(源代码)

//__begin, __end,__range has been used for explanation only.
{
auto && __range = range_expression ; 
for (auto __begin = begin_expr,
    __end = end_expr; 
    __begin != __end; ++__begin) {
    range_declaration = *__begin; 
    loop_statement 
    } 
} 

可以看到它在下面使用了begin和end迭代器。它只对定义了begin()和end()方法的类型有效,而对于stack则不是这样。

这是一个重复问题;参见std::stack暴露迭代器吗?对于答案——std::stack没有迭代器

std::stack提供了一个有意的限制包装容器的功能。

对于仍然需要迭代容器元素的情况,std::stack提供包装容器作为protected成员 c

protected意味着您需要定义一个派生类,以便在不使用低级技巧的情况下访问它。下面的代码可能仍然被认为是相当低级的,但它没有任何强制类型转换,没有显式转换。在某种程度上,我认为这很好,尽管它只是利用了c++类型系统中的漏洞。

#include <stack>
#include <iostream>
using namespace std;
template< class Item >
struct Hack
    : stack<Item>
{
    static
    auto container_of( stack<Item> const& st )
        -> typename stack<Item>::container_type const&
    { return st.*(&Hack::c); }
};
namespace std
{
    template< class Item >
    auto begin( stack<Item> const& s )
        -> decltype( Hack<Item>::container_of( s ).begin() )
    { return Hack<Item>::container_of( s ).begin(); }
    template< class Item >
    auto end( stack<Item> const& s )
        -> decltype( Hack<Item>::container_of( s ).end() )
    { return Hack<Item>::container_of( s ).end(); }
}
auto main()
    -> int
{
    stack<int> s;
    s.push(1);
    s.push(2);
    for( int const v : s ) { cout << v << endl; }
}

这个hack的优点是它可以在任何你需要迭代std::stack的项的地方使用,例如为了调试的目的。

但是对于普通的编程,我会从std::stack中派生出我自己的堆栈,我想这就是最初的意图。

可以这样写:

#include <stack>
#include <iostream>
using namespace std;
template< class Item >
class Stack
    : public std::stack<Item>
{
protected:
    using std::stack<Item>::c;
public:
    auto begin() const -> decltype( c.cbegin() ) { return c.cbegin(); }
    auto end() const -> decltype( c.cend() ) { return c.cend(); }
};
auto main()
    -> int
{
    Stack<int> s;
    s.push(1);
    s.push(2);
    for( int const v : s ) { cout << v << endl; }
}