C++ 为什么 std::queue::pop 不返回值。?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25035691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:13:43  来源:igfitidea点击:

Why doesn't std::queue::pop return value.?

c++stl

提问by cbinder

I went through this pagebut I am not able to get the reason for the same . There it is mentioned that

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

"it is more sensible for it to return no value at all and to require clients to use front() to inspect the value at the front of the queue"

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

But inspecting an element from front() also required that element to be copied in lvalue. For example in this code segment

但是从 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 */

/* 这里临时将在 RHS 上创建,并将分配给结果,如果通过引用返回,则结果将在弹出操作后呈现无效 */

result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

on fifth line coutobject first creates a copy of myqueue.front() then assigns that to result. So, whats the difference, pop function could have done the same thing.

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

采纳答案by utnapistim

So, whats the difference, pop function could have done the same thing.

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

It could indeed have done the same thing. The reason it didn't, is because a pop that returned the popped element is unsafe in the presence of exceptions (having to return by value and thus creating a copy).

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

Consider this scenario (with a naive/made up pop implementation, to ilustrate my point):

考虑这种情况(使用一个天真的/虚构的 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
    }

If the copy constructor of T throws on return, you have already altered the state of the queue (top_positionin my naive implementation) and the element is removed from the queue (and not returned). For all intents and purposes (no matter how you catch the exception in client code) the element at the top of the queue is lost.

如果 T 的复制构造函数在返回时抛出,则您已经更改了队列的状态(top_position在我的幼稚实现中)并且元素从队列中移除(并且未返回)。对于所有意图和目的(无论您如何在客户端代码中捕获异常),队列顶部的元素都将丢失。

This implementation is also inefficient in the case when you do not need the popped value (i.e. it creates a copy of the element that nobody will use).

在不需要弹出值的情况下,此实现也效率低下(即它创建了一个没人会使用的元素副本)。

This can be implemented safely and efficiently, with two separate operations (void popand const T& front()).

这可以通过两个单独的操作 (void popconst T& front())安全有效地实现。

回答by BPF2010

The page you have linked to answers your question.

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

To quote the whole section relevant:

引用整个相关部分:

One might wonder why pop() returns void, instead of value_type. That is, why must one use front() and pop() to examine and remove the element at the front of the queue, instead of combining the two in a single member function? In fact, there is a good reason for this design. If pop() returned the front element, it would have to return by value rather than by reference: return by reference would create a dangling pointer. Return by value, however, is inefficient: it involves at least one redundant copy constructor call. Since it is impossible for pop() to return a value in such a way as to be both efficient and correct, it is more sensible for it to return no value at all and to require clients to use front() to inspect the value at the front of the queue.

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

C++ is designed with efficiency in mind, over the number of lines of code the programmer has to write.

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

回答by Neil Kirk

pop cannot return a reference to the value that is removed, as it is being removed from the data structure, so what should the reference refer to? It could return by value, but what if the result of pop is not stored anywhere? Then time is wasted copying the value unnecessarily.

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

回答by Jan Rüegg

With the current implementation, this is valid:

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

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

If pop would return a reference, like this:

如果 pop 会返回一个引用,像这样:

value_type& pop();

Then the following code could crash, since the reference is not valid anymore:

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

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

On the other hand, if it would return a value directly:

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

value_type pop();

Then you would need to do a copy for this code to work, which is less efficient:

然后,您需要复制此代码才能工作,但效率较低:

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

回答by Vyacheslav Putsenko

Starting from C++11 it would possible to archive desired behavior using move semantics. Like pop_and_move. So copy constructor will not be called, and performance will depend on move constructor only.

从 C++11 开始,可以使用移动语义来存档所需的行为。喜欢pop_and_move。因此不会调用复制构造函数,性能将仅取决于移动构造函数。

回答by Masse Nicolas

I think the best solution would be to add something like

我认为最好的解决方案是添加类似的东西

std::queue::pop_and_store(value_type& value);

where value will receive the popped value.

其中 value 将接收弹出的值。

The advantage is that it could be implemented using a move assignment operator, while using front + pop will make a copy.

优点是它可以使用移动赋值运算符来实现,而使用 front + pop 将进行复制。

回答by xtofl

You can totally do this:

你完全可以这样做:

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

Or, if you want the value in a variable, use a reference:

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

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

Next to that: the wording 'more sensible' is a subjective form of 'we looked into usage patterns and found more need for a split'. (Rest assured: the C++ language is not evolving lightly...)

其次:“更明智”一词是“我们研究了使用模式并发现更需要拆分”的主观形式。(请放心:C++ 语言不会轻易进化……)