为什么不弹出返回值?

我通过这个 呼叫,但我不能得到相同的原因。这里提到

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

但是从 front ()检查元素也需要将该元素复制到 lvalue 中。例如,在此代码段中

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对象首先创建 myquee.front ()的副本,然后将其赋值给 result。所以,有什么区别呢,弹出函数也可以做同样的事情。

77284 次浏览

你完全可以这样做:

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

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

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

接下来: “更明智”这个词是“我们研究了使用模式,发现更有必要进行拆分”的主观形式。(请放心: C + + 语言并不是随意发展的... ...)

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

在当前的实现中,这是有效的:

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;

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

引用整个相关章节:

有人可能想知道为什么 pop ()返回 void 而不是 value _ type。也就是说,为什么必须使用 front ()和 pop ()来检查和删除队列前面的元素,而不是在单个成员函数中组合这两个元素?事实上,这个设计有一个很好的理由。如果 pop ()返回 front 元素,它必须通过值而不是通过 reference 返回: return by reference 将创建一个迷途指针。然而,按价值计算的回报率是低效的,因为它至少涉及一个冗余的复制建构子。因为 pop ()不可能以既有效又正确的方式返回值,所以更明智的做法是根本不返回任何值,并要求客户端使用 front ()检查队列前面的值。

C + + 的设计考虑到了效率,超过了程序员必须编写的代码行数。

所以,有什么区别呢,弹出函数也可以做同样的事情。

它确实可以做同样的事情。之所以没有返回,是因为返回弹出元素的 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 popconst T& front())安全有效地实现。

从 C + + 11开始,可以使用 move 语义来归档所需的行为。就像 pop_and_move。所以不会调用复制建构子,性能只取决于 move 构造函数。