为什么 std::queue::pop 没有返回值?

问题描述 投票:0回答:6

我浏览了这个页面,但我无法得到相同的原因。那里提到了

“更明智的是它根本不返回任何值并要求 客户端使用 front() 检查队列前面的值”

但是从 front() 检查元素还需要将该元素复制到左值中。例如在这段代码中

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.push (myint);
/* here temporary will be created on RHS which will be assigned to
   result, and in case if returns by reference then result will be
   rendered invalid after pop operation */
result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

第五行 cout 对象首先创建 myqueue.front() 的副本,然后将其分配给 result。那么,有什么区别呢,pop 函数可以做同样的事情。

c++ stl
6个回答
144
投票

那么,有什么区别,pop 函数可以做同样的事情。

它确实可以做同样的事情。之所以没有,是因为返回弹出元素的 pop 在存在异常的情况下是不安全的(必须按值返回,从而创建副本)。

考虑这个场景(用一个天真的/虚构的流行实现,来说明我的观点):

template<class T>
class queue {
    T* elements;
    std::size_t top_position;
    // stuff here
    T pop()
    {
        auto x = elements[top_position];
        // TODO: call destructor for elements[top_position] here
        --top_position;  // alter queue state here
        return x;        // calls T(const T&) which may throw
    }

如果 T 的复制构造函数在返回时抛出异常,则说明您已经更改了队列的状态(在我的幼稚实现中为

top_position
),并且该元素已从队列中删除(并且不返回)。出于所有意图和目的(无论您如何在客户端代码中捕获异常),队列顶部的元素都会丢失。

当您不需要弹出的值时(即它创建了没有人会使用的元素的副本),这种实现也是低效的。

这可以通过两个单独的操作(

void pop
const T& front()
)安全有效地实施。


53
投票

您链接到的页面回答了您的问题。

引用整个相关部分:

有人可能想知道为什么 pop() 返回 void,而不是 value_type。也就是说,为什么必须使用 front() 和 pop() 来检查和删除队列前面的元素,而不是将两者组合在一个成员函数中?事实上,这样的设计是有充分理由的。如果 pop() 返回前面的元素,则必须按值返回而不是按引用返回:按引用返回会创建一个悬空指针。然而,按值返回的效率很低:它至少涉及一次冗余的复制构造函数调用。由于 pop() 不可能以既高效又正确的方式返回值,所以更明智的做法是根本不返回任何值,并要求客户端使用 front() 检查值队列的前面。

C++ 的设计考虑了效率,而不是程序员必须编写的代码行数。


8
投票

pop 无法返回对被删除值的引用,因为它正在从数据结构中删除,那么该引用应该引用什么?它可以按值返回,但是如果 pop 的结果没有存储在任何地方怎么办?然后时间就浪费在不必要的复制值上。


6
投票

从 C++11 开始,可以使用移动语义实现所需的行为。就像

pop_and_move
。因此复制构造函数不会被调用,性能仅取决于移动构造函数。


4
投票

对于当前的实施,这是有效的:

int &result = myqueue.front();
std::cout << result;
myqueue.pop();

如果 pop 返回一个引用,如下所示:

value_type& pop();

那么以下代码可能会崩溃,因为引用不再有效:

int &result = myqueue.pop();
std::cout << result;

另一方面,如果它直接返回一个值:

value_type pop();

那么你需要复制一份代码才能工作,这样效率较低:

int result = myqueue.pop();
std::cout << result;

0
投票

你完全可以做到这一点:

std::cout << ' ' << myqueue.front();

或者,如果您想要变量中的值,请使用引用:

const auto &result = myqueue.front();
if (result > whatever) do_whatever();
std::cout << ' ' << result;

接下来:“更明智”这个措辞是“我们研究了使用模式并发现更需要拆分”的主观形式。 (放心:C++ 语言的发展并不轻松……)

© www.soinside.com 2019 - 2024. All rights reserved.