1

I have a class which has-a deque which it uses as a stack (could be a vector, just happened to choose deque).

At any rate, I wish to allow consumers to iterate over the contents of the stack (something that std::stack cannot do). I am currently using push_back() to push items onto the stack, and hence if one iterates over the contents in forward ordering, one goes from bottom to the top of the stack.

I would prefer to expose things in reverse order, so that iterating over the stack using for (auto e: thestack) works in top-down fashion.

I found C++11 reverse range-based for-loop which shows a couple of solutions for reversing the iteration ordering for the new for-loop syntax (as well as range-based algorithms).

What is unclear to me is how to provide a simple mechanic for giving my users access to this automatically reversed deque.

e.g. without any real effort, I can simply allow const& access to the underlying deque:

  const std::deque<T> & GetStack() const { return m_stack; }

and consumers could be responsible for the reversing:

  for (auto e : reverse(m_toolstack.GetStack()))

Here, I am attempting to use the following solution for reverse:

template<class Fwd>
struct reverser_generic 
{
    Fwd &fwd;
    reverser_generic(Fwd& fwd_): fwd(fwd_) {}
    typedef std::reverse_iterator<typename Fwd::iterator> reverse_iterator;
    reverse_iterator begin() { return reverse_iterator(std::end(fwd)); } 
    reverse_iterator end() { return reverse_iterator(std::begin(fwd)); } 
};

template<class Fwd>
struct reverser_special
{
    Fwd &fwd;
    reverser_special(Fwd& fwd) : fwd(fwd) { }
    auto begin() -> decltype(fwd.rbegin()) { return fwd.rbegin(); } 
    auto end() -> decltype(fwd.rend()) { return fwd.rend(); } 
};

template<class Fwd>
auto reverse_impl(Fwd& fwd, long) -> decltype(reverser_generic<Fwd>(fwd))
{ 
    return reverser_generic<Fwd>(fwd);
}

template<class Fwd>
auto reverse_impl(Fwd& fwd, int) -> decltype(fwd.rbegin(), reverser_special<Fwd>(fwd))
{ 
    return reverser_special<Fwd>(fwd);
}

template<class Fwd>
auto reverse(Fwd&& fwd) -> decltype(reverse_impl(fwd,int(0))) 
{
    static_assert(!(std::is_rvalue_reference<Fwd&&>::value), "Cannot pass an rvalue reference to reverse()");
    return reverse_impl(fwd,int(0));
}

The above all compiles and runs correctly using VS2012 (thanks to Jive Dadson et. al. for that solution).

However, in my stack facade, I really want to simply always return the reverse of the underlying container, but I am unclear on how to do so in a sensible fashion:

auto GetContents() const -> decltype(reverse(m_stack)) { return reverse(m_stack); }

The above errors, indicating that m_stack is unknown. this->m_stack is equally unknown.

How might I go about returning the "whatever it is that is the reverse of my member m_stack"?

Note, once that is answered, I also need "How do I return the const & of whatever is the decltype of reverse(m_stack)?

Community
  • 1
  • 1
Mordachai
  • 9,412
  • 6
  • 60
  • 112
  • 1
    `decltype(reverse(std::declval()))` – Xeo Feb 12 '13 at 15:18
  • Why not create proper iterators for your class instead? – Some programmer dude Feb 12 '13 at 15:20
  • Yes, that has occurred to me as well. Laziness - and a certain stubbornness to figure this out now that I've gone down this path. :) – Mordachai Feb 12 '13 at 15:23
  • @Xeo That works for non-const. And now I see that declval is the trick to go from type to value-type, which is cool! but how do deal with constness of all of this? How can I get a const version of GetContents() using auto and -> decltype() syntax? – Mordachai Feb 12 '13 at 15:31
  • 1
    `std::declval()`? – Xeo Feb 12 '13 at 15:46
  • Thanks. If you want to put that as an answer, it works. – Mordachai Feb 12 '13 at 15:48
  • @Xeo, I would use `std::declval()` so it matches the value category of the member `m_stack`, just in case the `reverse` gets updated to distinguish between lvalues and rvalues – Jonathan Wakely Feb 12 '13 at 17:34
  • @JonathanWakely Can you explain - or point me to an explanation, that makes clear what you're saying here? I guess I don't understand the implication... – Mordachai Feb 12 '13 at 18:26
  • 1
    The return type of `std::declval()` is `X` which is an rvalue expression, but the return type of `std::declval()` is `X&` which is an lvalue expression. The function body calls `reverse(m_stack)` and `m_stack` is an lvalue. If your `reverse` function handled rvalues and lvalues differently then `decltype(reverse(std::declval()))` would not be the same type as `decltype(reverse(m_stack))` and the function would fail to compile. Changing the `std::declval` expression to give an lvalue result makes it consistent with the function body. – Jonathan Wakely Feb 13 '13 at 11:49

1 Answers1

1

Is the declaration of GetContents before the declaration of m_stack?

Names in a late-specified return type must obey the same rules as names in the rest of the function signature, i.e. they cannot refer to class members that haven't been declared yet:

struct X
{
    auto f() -> decltype(m_x) { return m_x; }  // ERROR
    int m_x;
    auto g() -> decltype(m_x) { return m_x; }  // OK
};

To make it work, either use std::declval to refer to the type (which means you must update the signature of GetContents if you change the type of m_stack from std::deque to std::vector) or ensure m_stack has been declared before you refer to it.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • d'oh! I had no idea that this would work! Frustrating that this would be required here, but once you know... I guess it's okay. – Mordachai Feb 12 '13 at 18:00
  • 1
    N.B. a late-specific return type is useful to refer to the function parameters, but you're not doing that, so could just as easily write it `decltype(m_stack) GetContents() const` ... then it might be a bit more understandable that `m_stack` must be in scope, just like if you'd tried to write `stack_type GetContents() const;` referring to a typedef which hadn't been declared yet. – Jonathan Wakely Feb 12 '13 at 18:12