35

Program 1:

#include <iostream>
#include <cstdlib>
#include <vector>

int main(){

    //compiles successfully 
    std::vector<int> vec{1,2,3,4,5};

    return EXIT_SUCCESS;
}

Program 2:

#include <iostream>
#include <cstdlib>
#include <queue>

int main(){

    //compiler error
    std::queue<int> que{1,2,3,4,5};

    return EXIT_SUCCESS;
}

Error message:

main.cpp: In function ‘int main()’:
main.cpp:7:31: error: no matching function for call to ‘std::queue<int>::queue(<brace-enclosed initializer list>)’
main.cpp:7:31: note: candidates are:
/usr/include/c++/4.6/bits/stl_queue.h:141:7: note: std::queue<_Tp, _Sequence>::queue(_Sequence&&) [with _Tp = int, _Sequence = std::deque<int, std::allocator<int> >]
/usr/include/c++/4.6/bits/stl_queue.h:141:7: note:   candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:137:7: note: std::queue<_Tp, _Sequence>::queue(const _Sequence&) [with _Tp = int, _Sequence = std::deque<int, std::allocator<int> >]
/usr/include/c++/4.6/bits/stl_queue.h:137:7: note:   candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: std::queue<int>::queue(const std::queue<int>&)
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note:   candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: std::queue<int>::queue(std::queue<int>&&)
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note:   candidate expects 1 argument, 5 provided

Question:
why can't queues be initialized like vectors?
I suppose they aren't sequence containers, but why would that matter?
I'm sure there is a good reason, but I can't find any explanations.

gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • You can check the reference on containers [here](http://www.cplusplus.com/reference/stl/). You may also like to look at the boost::assign library [here](http://www.boost.org/doc/libs/1_42_0/libs/assign/doc/index.html). – czar x Apr 24 '12 at 07:40

3 Answers3

45
queue<int> q({1, 2, 3});
Jon
  • 632
  • 5
  • 13
  • 7
    this is a good one, I suppose it works because one of queue's constructors accepts the underlying container type. initializer list is firstly converted to that, as compiler is allowed to do one implicit conversion, and then the queue's constructor is called with the initialized container – iggy Feb 05 '16 at 04:47
  • 4
    This also works for `std::stack({1, 2, 3})` but a priority queue needs a comparison functor as the first argument. If the functor can be default constructed: `std::priority_queue({}, {1, 2, 3 })` – jgawrych Apr 30 '18 at 18:25
39

I don't think it really has anything to do with being container adapters rather than containers (though I'll admit I'm uncertain exactly why the correct constructor is omitted).

When you use a braced initializer list with std::vector, you're using this (new in C++11) constructor:

vector(initializer_list<T>, const Allocator& = Allocator());

Looking at the definition of std::queue, the available constructors are:

explicit queue(const Container&);
explicit queue(Container&& = Container());
template <class Alloc> explicit queue(const Alloc&);
template <class Alloc> queue(const Container&, const Alloc&);
template <class Alloc> queue(Container&&, const Alloc&);
template <class Alloc> queue(const queue&, const Alloc&);
template <class Alloc> queue(queue&&, const Alloc&);

A constructor taking an initialization_list is conspicuously absent.

I'm quite certain that despite being a container adapter, such a constructor would be trivial if it was desired. Just for example:

#include <deque>
#include <initializer_list>
#include <iostream>

template <class T, class container=std::deque<T> >
class myqueue {
    container data;
public:
    explicit myqueue(std::initializer_list<T> t) : data(t) {}
    void pop() { data.pop_front(); }
    T front() const { return data.front(); }
    bool empty() const { return data.empty(); }
};

int main(){
    myqueue<int> data {1, 2, 3, 4};
    while (!data.empty()) {
        std::cout << data.front() << "\n";
        data.pop();
    }
    return 0;
}

g++ 4.7 accepts this without any problems, and produces exactly the output you'd expect:

1
2
3
4

Although I haven't tested with any other compilers, I can't see any reason other compilers wouldn't work fine with this as well (provided they implement the necessary features, of course).

Edit: I just did some looking through the committee papers proposing the addition of initalizer_lists to C++ (e.g., N1890, N1919, N2100, N2215, N2220) and it looks to me like a simple oversight. Many of the earlier papers are more conceptual, but N2220 has a fair amount of proposed language for the working paper. For std::array (for one example) it specifically points out that no change is needed. It then goes through deque, vector, [unordered_][multi_](set|map), and shows changes needed for each -- but no mention is made of stack or queue at all, in either direction. No proposal to add support for std::initializer_list, nor (like std::array) reasoning for their omission.

I'd conclude that it was a simple oversight, that probably slipped through for two reasons: 1) the adapters are almost, but not quite containers, and 2) the adapter classes don't seem to be used a whole lot, so forgetting about them was probably fairly easy (and, of course, the ever-pervasive third reason: most of the active committee members are horribly overworked).

Edit2: I should probably add one more detail: since stack and queue can both accept another container for the initialization, you can pretty easily do something like:

std::stack<int> data(std::vector<int>{1,2,3,4});

This is somewhat verbose, but unlikely to cause any loss of efficiency (the container will be passed as an rvalue reference, so its representation will be "stolen" instead of copied). There is one caveat though: if the type of container you use doesn't match the container underlying the container adapter, you'll get a copy rather than a move (and consequently, may lose some efficiency).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 2
    Instead of implementing all yourself, you could inherit the container adaptor you need, and add the initializer-list constructor. Use C++11 forwarding constructors so you don't have to implement the base-class constructors. – Some programmer dude Apr 24 '12 at 08:32
  • 2
    Such an under voted answer for offering two viable solutions plus a summary of the corresponding papers. – Ad N Oct 30 '13 at 16:48
  • 1
    @Someprogrammerdude: You could, but when the whole class (with added ctor) is only 9 lines of code, it's probably open to question whether it makes enough difference to bother. – Jerry Coffin Apr 20 '23 at 06:50
12

std::queue and std::stack are not actually containers, they are so called container adaptors which uses a container (by default std::deque). Therefore you can not initialize it as other containers.

Edit

For a container to be able to use an initializer list, it must have a constructor taking an std::initializer_list as argument. The container adaptors don't do that. If it's deliberate or an oversight of the standards committee is up to anyones interpretation.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 2
    In short `std::queue`, `std::stack` & `std::priorityqueue` are *Container Adaptors*, Containers built by using other standard library containers. – Alok Save Apr 24 '12 at 07:15
  • 2
    Still, they could call the underlying container with the initialization list. Is there a reason why this is not done? – RedX Apr 24 '12 at 07:26
  • I can initialize std::deque. Aren't these adapters pretty much deques with less functionality? also, what @RedX said – Trevor Hickey Apr 24 '12 at 07:29
  • @RedX: Could they? In theory, yes; the adapters could have had initializer list constructors that pass the values to the underlying container. If you think it's a defect, you could pass it off to the C++ committee. – Nicol Bolas Apr 24 '12 at 08:21
  • Nice, this *should* be the accepted answer because the accepted answer's first sentence contradicts this one, but this one is correct & straight to the point. – nevelis Sep 15 '15 at 00:04