I went through this page but 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()中的元素也需要在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 */
/*在这里临时将在RHS上创建,并分配给result,如果引用返回,那么在pop操作*/之后,result将被作废
result = myqueue.front(); //result.
std::cout << ' ' << result;
myqueue.pop();
on fifth line cout object first creates a copy of myqueue.front() then assigns that to result. So, whats the difference, pop function could have done the same thing.
在第5行,cout对象首先创建queue.front()的副本,然后将其分配给result。那么,有什么区别呢,pop函数可以做同样的事情。
7 个解决方案
#1
71
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_position
in 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 pop
and const T& front()
).
这可以通过两个独立的操作(void pop和const t&front()安全地和有效地实现。
#2
21
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++设计时考虑到了效率,而不是程序员必须编写的代码行数。
#3
3
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的结果没有存储在任何地方呢?然后,不必要地复制值就浪费了时间。
#4
2
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;
#5
0
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++语言的发展并不轻松……)
#6
0
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.
值将接收弹出的值。
The advantage is that it could be implemented using a move assignment operator, while using front + pop will make a copy.
其优点是可以使用move赋值操作符实现,而使用front + pop将复制一个副本。
#7
0
Starting from Cx11 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.
从Cx11开始,可以使用移动语义归档所需的行为。像pop_and_move。因此不会调用复制构造函数,性能仅取决于move构造函数。
#1
71
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_position
in 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 pop
and const T& front()
).
这可以通过两个独立的操作(void pop和const t&front()安全地和有效地实现。
#2
21
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++设计时考虑到了效率,而不是程序员必须编写的代码行数。
#3
3
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的结果没有存储在任何地方呢?然后,不必要地复制值就浪费了时间。
#4
2
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;
#5
0
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++语言的发展并不轻松……)
#6
0
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.
值将接收弹出的值。
The advantage is that it could be implemented using a move assignment operator, while using front + pop will make a copy.
其优点是可以使用move赋值操作符实现,而使用front + pop将复制一个副本。
#7
0
Starting from Cx11 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.
从Cx11开始,可以使用移动语义归档所需的行为。像pop_and_move。因此不会调用复制构造函数,性能仅取决于move构造函数。