4

The forward declaration of STL library class std::queue is as follows:

namespace std {
    template<typename T, class container = deque<T>> class queue
}

That means we can declare an object of type queue with different type specifications like this:

std::queue<float, std::deque<std::string>> string_queue;

Why this is possible? Wouldn't it be much more type safe to declare queue like this:

template<class implementation>
class queue_base
{
private:
    implementation m_impl;
    /* ----------------------------------------------------------- */

public:
    typedef implementation                      container_type;
    typedef typename implementation::size_type  size_type;
    typedef queue_base<implementation>          this_type;
    typedef typename implementation::value_type value_type;
    /* ----------------------------------------------------------- */

    queue_base         ();
    queue_base         (queue_base const& other);
    explicit queue_base(container_type const& other);
    /* ----------------------------------------------------------- */

    value_type&       back ();
    value_type const& back () const;
    bool              empty() const;
    value_type&       front();
    value_type const& front() const;
    void              pop  ();
    void              push (value_type const& value);
    size_type         size () const;
    /* ----------------------------------------------------------- */
}; /* template<class> class queue_base */
/* --------------------------------------------------------------- */

Most implementations of std::queue I have seen implement 'value_type' and 'size_type' in the same manner as you can see in my code above. So, template parameter 'T' is only been used for the default parameter of template parameter 'container' (std::deque).

I mean, I don't think it's "fine" that the float specification in declaration example above is ignored; no matter it works or not.

0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • See [this answer](http://stackoverflow.com/questions/4962518/templates-and-stl/4962708#4962708) as well. – GManNickG Apr 18 '11 at 10:32

3 Answers3

3

Wouldn't it be more type safe to declare queue like this:

More type safe yes, but not as convenient. The normal user of queue doesn’t care about the underlying container which is really just an implementation detail. They only care about the element type. This is also for consistency with the other container classes.

Even better would be if the queue class were usable by specifying a class template for the container, as follows:

std::queue<int, std::list> myqueue;

But there’s unfortunately no good, portable way of doing this in C++.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Hum, I think that's a weak point. When I'm using std::list instead of std::deque as container for std::queue I also need to fully type std::queue>. – 0xbadf00d Apr 18 '11 at 10:20
  • 1
    @Sascha But you *don’t*, usually, use a `std::list`. The normal use-case for the queue is to use the default, i.e. `std::queue`. That’s not really a weak point, optimising for the common case is a good design principle. – Konrad Rudolph Apr 18 '11 at 10:21
  • Yeah, I know what you mean, but in my point of view, you shouldn't prefer optimization (in any kind) over integrity. – 0xbadf00d Apr 18 '11 at 10:26
  • @Sascha: Huh? What integrity is being lost? – GManNickG Apr 18 '11 at 10:29
  • @Konrad: FWIW, you can in C++0x. I did it somewhere else as an answer, I'll see if I can find it. EDIT: [Here](http://stackoverflow.com/questions/4962518/templates-and-stl/4962708#4962708). – GManNickG Apr 18 '11 at 10:29
  • @std::queue myqueue; Why? You could do it like this: template class implementation> class queue>; @GMan I've spoken from "type integrity". – 0xbadf00d Apr 18 '11 at 10:30
  • @Gman Yes, I thought about mentioning this (and template templates) but decided against it. – Konrad Rudolph Apr 18 '11 at 10:33
  • @Sascha: in C++03, you have to specify the full arity of the template template parameter. Which precludes using it with a template class of another arity (unfortunately). – Matthieu M. Apr 18 '11 at 10:34
  • @Sascha “you shouldn't prefer optimization (in any kind) over integrity” – the opposite is true: you *must*. In theory I’m very much on your side but in reality, every decision implies a trade-off and you *need* to make some concessions. The issue you’ve mentioned is very irksome for an ideal interface but in reality it just doesn’t pose a problem: the interface is clearly documented and there is almost no risk of misuse; or even if there is, the error is very quickly apparent. – Konrad Rudolph Apr 18 '11 at 10:35
  • @Sascha concerning the `template class …` – have you tried that? It doesn’t work. – Konrad Rudolph Apr 18 '11 at 10:37
1

Logically, one might expect something like:

template<typename T, template<typenameU> class C = std::deque>
class queue
{
protected:
    C<T> c;
public:
    //  ...
};

The problem is that std::deque won't match the template argument, because it is, in fact:

template<typename T, typename Allocator = allocator<T> >
class deque ...

The extra template argument prevents it from working. And if the extra argument were added to the second parameter of queue, then most user defined containers couldn't be used, because they wouldn't have the second argument. The current solution sidesteps that problem (while still allowing client code to instantiate std::queue<float>, for example, without worrying about the underlying container type).

And in the end, why not? Your example of std::queue<float, std::deque<std::string> > probably won't compile, but what's wrong with something like std::queue<bool, std::vector<char> > (avoiding the problematic std::vector<bool>)? As long as there's an implicit conversion both ways, it's up to the client to ensure that it does what he wants. (In practice, of course, it's almost never a problem, because it's rare for client code to specify the container.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Like I said above: In most implementation the first template parameter is ignored. So, 'bool' has no meaning in your example. – 0xbadf00d Apr 18 '11 at 10:35
  • So I see. In fact, this is required by the standard. It seems rather strange to me, though; I would have expected exactly the opposite. (Or some sort of static assert that the two types were the same.) – James Kanze Apr 18 '11 at 11:15
1

If you only care about preventing "silly" cases, a simple integrity check is sufficient:

template <typename T, typename Container>
class queue
{
  static_assert(std::is_same<T, typename Container::value_type>::value,
    "queue require T and Container::value_type to be identical");
};

Or similar facilities in C++03.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722