【问题标题】:Why can't I construct a queue/stack with brace-enclosed initializer lists? (C++11)为什么我不能用大括号括起来的初始化列表构造队列/堆栈? (C++11)
【发布时间】:2012-05-04 19:37:28
【问题描述】:

方案一:

#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

问题:
为什么队列不能像向量一样初始化?
我想它们不是序列容器,但这有什么关系呢?
我确信有充分的理由,但我找不到任何解释。

gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1

【问题讨论】:

  • 您可以查看容器上的引用here。您可能还想查看 boost::assign 库here

标签: c++ vector c++11 initialization queue


【解决方案1】:
queue<int> q({1, 2, 3});

【讨论】:

  • 这是一个很好的方法,我想它可以工作,因为队列的构造函数之一接受底层容器类型。初始化器列表首先转换为那个,因为允许编译器进行一次隐式转换,然后使用初始化的容器调用队列的构造函数
  • 这也适用于std::stack({1, 2, 3}),但优先级队列需要一个比较函子作为第一个参数。如果函子可以默认构造:std::priority_queue&lt;int&gt;({}, {1, 2, 3 })
【解决方案2】:

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

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

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

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

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 的构造函数明显不存在。

我很确定,尽管它是一个容器适配器,但如果需要,这样的构造函数将是微不足道的。举个例子:

#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 毫无问题地接受了这一点,并准确地产生了您期望的输出:

1
2
3
4

虽然我没有使用任何其他编译器进行测试,但我看不出其他编译器无法正常工作的任何原因(当然,前提是它们实现了必要的功能)。

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

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

Edit2:我可能应该再补充一点:因为stackqueue 都可以接受另一个容器进行初始化,所以您可以很容易地执行以下操作:

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

这有点冗长,但不太可能导致任何效率损失(容器将作为右值引用传递,因此其表示将被“窃取”而不是复制)。但是有一个警告:如果您使用的容器类型与容器适配器下的容器不匹配,您将得到一个副本而不是移动(因此,可能会失去一些效率)。

【讨论】:

  • 你可以继承你需要的容器适配器,并添加初始化列表构造函数,而不是自己实现。使用 C++11 转发构造函数,因此您不必实现基类构造函数。
  • 这样一个投票不足的答案提供了两个可行的解决方案以及相应论文的摘要。
【解决方案3】:

std::queuestd::stack 实际上并不是容器,它们被称为容器适配器,它们使用容器(默认为std::deque)。因此不能将其初始化为其他容器。

编辑

对于能够使用初始化列表的容器,它必须有一个以std::initializer_list 作为参数的构造函数。容器适配器不这样做。如果这是故意的或对标准委员会的疏忽取决于任何人的解释。

【讨论】:

  • 简而言之std::queuestd::stackstd::priorityqueueContainer Adaptors,使用其他标准库容器构建的容器。
  • 仍然可以使用初始化列表调用底层容器。没有这样做是有原因的吗?
  • 我可以初始化std::deque。这些适配器不是功能较少的双端队列吗?还有,@RedX 所说的
  • @RedX:可以吗?理论上,是的;适配器可能具有将值传递给底层容器的初始化列表构造函数。如果您认为这是一个缺陷,您可以将其提交给 C++ 委员会。
  • 很好,这应该是被接受的答案,因为被接受的答案的第一句话与这句话相矛盾,但这句话是正确且直截了当的。
猜你喜欢
  • 2016-02-25
  • 1970-01-01
  • 2012-11-21
  • 2021-12-31
  • 2013-08-23
  • 1970-01-01
  • 2016-12-11
  • 2020-06-02
  • 1970-01-01
相关资源
最近更新 更多