为什么我不能用大括号括起来的初始化列表构造队列/堆栈?(C++11)

2022-01-19 00:00:00 queue initialization vector c++ c++11

方案一:

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

int main(){

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

    return EXIT_SUCCESS;
}

<小时>

方案 2:

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

int main(){

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

    return EXIT_SUCCESS;
}

错误信息:

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

gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1

推荐答案

我不认为它与容器适配器而不是容器有任何关系(尽管我承认我不确定为什么正确的构造函数被省略).

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).

当您使用带有 std::vector 的大括号初始化列表时,您正在使用这个(C++11 中的新)构造函数:

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());

std::queue的定义,可用的构造函数有:

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&);

采用initialization_list 的构造函数明显不存在.

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() << "
";
        data.pop();
    }
    return 0;
}

g++ 4.7 毫无问题地接受了这一点,并准确地产生了您期望的输出:

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).

我刚刚浏览了一些提议在 C++ 中添加 initalizer_lists 的委员会文件(例如,N1890、N1919、N2100、N2215、N2220),在我看来这只是一个简单的疏忽.许多早期的论文更具概念性,但 N2220 为工作论文提供了相当多的建议语言.对于 std::array (例如),它特别指出不需要更改.然后它会遍历 dequevector[unordered_][multi_](set|map),并显示每个所需的更改 -- 但是在任何一个方向上都没有提到堆栈或队列.没有提议添加对 std::initializer_list 的支持,也没有(像 std::array 一样)对它们的省略进行推理.

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.

我的结论是这是一个简单的疏忽,可能有两个原因:1)适配器几乎是容器,但不完全是容器,2)适配器类似乎没有被大量使用,所以忘记它们可能相当容易(当然还有第三个普遍存在的原因:大多数活跃的委员会成员都过度劳累).

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:我可能应该添加更多细节:因为 stackqueue 都可以接受另一个容器进行初始化,您可以很容易地执行以下操作:

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).

相关文章