Confused about returns in stack template

128 Views Asked by At

I'm implementing a generic stack (with an array) in C++ and am confused about what to return in this situation:

template <class T>
T Stack<T>::pop(void) {
    if (size != 0) {
        return items[size - 1];
        size--;
    } else {
        cerr << "Cannot pop from empty stack." << endl;
        return ???;
    }
}

template <class T>
T Stack<T>::peek(void) {
    if (size != 0)
        return items[size - 1];
    else {
        cerr << "Cannot peek from empty stack." << endl;
        return ???;
    }
}

What are my options here? I think it would be messy to do something like declaring a new T variable and returning it. I'm drawing a blank.

2

There are 2 best solutions below

0
On BEST ANSWER

This depends on what you want the behaviour (protocol) of your class to be. Since you're logging into the error stream there, I assume you consider this an error condition to call pop() on an empty stack. The standard C++ way of signalling errors is to throw an exception. Something like this:

template <class T>
T Stack<T>::pop(void) {
    if (size != 0) {
        size--;
        return items[size];
    } else {
        throw std::invalid_argument("Cannot pop from empty stack.");
    }
}

An alternative would be to say that pop() has a precondition "stack is not empty." Violation of a precondition is generally undefined behaviour, so you could simply assume the stack is not empty. This is the useful approach for performance-critical code:

template <class T>
T Stack<T>::pop(void) {
    asssert(size > 0);  // not necessary, but it's good practice to assert preconditions
    size--;
    return items[size];
}

The above two approaches assume that calling pop() on an empty stack is an error, i.e. that it shouldn't happen. If you instead want that to be a valid operation with a well-defined result, you have a few other options.

Return a flag indicating success:

template <class T>
std::pair<T, bool> Stack<T>::pop(void) {
    if (size != 0) {
        size--;
        return std::make_pair(items[size], true);
    } else {
        return std::make_pair(T(), false);  // requires T to be default-constructible
    }
}

Return a boost::optional:

template <class T>
boost::optional<T> Stack<T>::pop(void) {
    if (size != 0) {
        size--;
        return items[size];
    } else {
        return boost::none;
    }
}

Return a default-constructed T:

template <class T>
T Stack<T>::pop(void) {
    if (size != 0) {
        size--;
        return items[size];
    } else {
        return T();
    }
}
0
On

Usually in such situations an exception is thrown.

Or you should change the return type of function pop to void.

As for the function peak then it could return a reference to an object in the stack.