std :: queue :: pop이 값을 반환하지 않는 이유는 무엇입니까?


123

페이지를 살펴 봤지만 같은 이유를 알 수 없습니다. 거기에 언급되어 있습니다

"아무 값도 반환하지 않고 클라이언트가 front ()를 사용하여 큐 맨 앞의 값을 검사하도록 요구하는 것이 더 합리적입니다."

그러나 front ()에서 요소를 검사하려면 해당 요소를 lvalue로 복사해야합니다. 예를 들어이 코드 세그먼트에서

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.push (myint);

/ * 여기서는 결과에 할당 될 RHS에 임시가 생성되며, 참조로 반환되는 경우 결과는 팝 작업 후 유효하지 않게 렌더링됩니다 * /

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

다섯 번째 줄에서 cout 객체는 먼저 myqueue.front ()의 복사본을 만든 다음 결과에 할당합니다. 그래서 차이점은 무엇입니까, 팝 기능은 같은 일을 할 수 있습니다.


이 방식으로 구현되기 때문입니다 (예 :) void std::queue::pop();.
101010 2014-07-30

질문은 대답하지만, 된 (!) 참고로 : 당신은 정말 반환, 그것은 쉽게 무료로 기능이 구현 될 수있는 팝업하려는 경우 : ideone.com/lnUzf6
eerorika

1
귀하의 링크는 STL 문서에 대한 것입니다. 그러나 C ++ 표준 라이브러리에 대해 질문하고 있습니다. 다른 것들.
juanchopanza

5
"하지만 요소를 검사 front()하려면 해당 요소를 lvalue로 복사해야합니다."-그렇지 않습니다. front값이 아닌 참조를 반환합니다. 참조하는 값을 복사하지 않고도 검사 할 수 있습니다.
마이크 시모어

1
@KeminZhou 설명하는 모델에는 사본이 필요합니다. 아마도. 큐의 소비를 다중화하려면 예, 큐에 대한 잠금을 해제하기 전에 사본을 작성해야합니다. 그러나 입력과 출력을 분리하는 데에만 관심이 있다면 전면을 검사하는 데 잠금 장치가 필요 하지 않습니다 . 사용을 마치고을 호출해야 할 때까지 잠글 때까지 기다릴 수 pop()있습니다. 사용하는 경우 std::queue<T, std::list<T>>제공된 참조 front()push(). 그러나 사용 패턴을 알고 있어야하며 제약 사항을 문서화해야합니다.
jwm

답변:


105

그래서 차이점은 무엇입니까, 팝 기능은 같은 일을 할 수 있습니다.

실제로 같은 일을 할 수있었습니다. 그렇지 않은 이유는 팝된 요소를 반환 한 팝이 예외가있을 때 안전하지 않기 때문입니다 (값으로 반환해야하고 따라서 복사본을 생성해야 함).

이 시나리오를 고려하십시오 (순진한 / 만들어진 팝 구현으로 내 요점을 설명하기 위해).

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())을 통해 안전하고 효율적으로 구현할 수 있습니다 .


흠 ... 그 의미가 있습니다
cbinder

37
C ++ 11 참고 : T에 값싼 noexcept move-constructor (종종 스택에 넣은 객체의 경우)가 있으면 값으로 반환하는 것이 효율적이고 예외에 안전합니다.
Roman L

9
@ DavidRodríguez-dribeas : 변경 사항을 제안하는 것이 아닙니다. 제 요점은 언급 된 단점 중 일부가 C ++ 11에서 문제가되지 않는다는 것입니다.
Roman L

11
그런데 왜 불린다 pop? 그것은 매우 반 직관적입니다. 그것은 이름이 될 수 있습니다 drop, 그리고 ... 다음은이 요소를 팝업하지 않는 모든 사람들에게 분명하지만, 대신 방울
UeliDeSchwert

12
@utnapistim : 사실상 "팝"작업은 항상 스택에서 최상위 요소를 가져 와서 반환하는 것이 었습니다. 처음 STL 스택을 발견했을 때 나는 pop아무 것도 반환하지 않는다는 사실에 놀랐습니다 . 예를 들어 wikipedia를 참조하십시오 .
Cris Luengo

34

링크 한 페이지가 질문에 대한 답변입니다.

관련 섹션 전체를 인용하려면 :

왜 pop ()이 value_type 대신 void를 반환하는지 궁금 할 것입니다. 즉, 단일 멤버 함수에서 두 요소를 결합하는 대신 큐 맨 앞의 요소를 검사하고 제거하기 위해 front () 및 pop ()을 사용해야하는 이유는 무엇입니까? 사실,이 디자인에는 타당한 이유가 있습니다. pop ()이 앞 요소를 반환하면 참조가 아닌 값으로 반환해야합니다. 참조로 반환하면 매달린 포인터가 생성됩니다. 그러나 값에 의한 반환은 비효율적입니다. 하나 이상의 중복 복사 생성자 호출이 필요합니다. pop ()이 효율적이고 정확한 방식으로 값을 반환하는 것은 불가능하기 때문에 값을 전혀 반환하지 않고 클라이언트가 front ()를 사용하여 값을 검사하도록 요구하는 것이 더 현명합니다. 대기열의 앞.

C ++는 프로그래머가 작성해야하는 코드 라인 수에 비해 효율성을 염두에두고 설계되었습니다.


16
아마도 실제 이유는 pop값을 반환하는 버전에 대해 예외 안전 버전 (강력한 보증 포함)을 구현하는 것이 불가능하기 때문입니다 .
James Kanze 2014

5

pop은 데이터 구조에서 제거되므로 제거 된 값에 대한 참조를 반환 할 수 없습니다. 참조가 무엇을 참조해야합니까? 값으로 반환 할 수 있지만 pop의 결과가 어디에도 저장되지 않으면 어떻게 될까요? 그러면 불필요하게 값을 복사하는 데 시간이 낭비됩니다.


4
진짜 이유는 예외 안전입니다. pop반환 및 반환 동작으로 인해 예외가 발생할 수있는 경우 스택과 함께 "트랜잭션"을 수행 할 수있는 안전한 방법은 없습니다 (요소가 스택에 남아 있거나 사용자에게 반환 됨) . 분명히 요소를 반환하기 전에 제거해야하며, 무언가가 던져지면 요소가 돌이킬 수 없게 손실 될 수 있습니다.
Jon

1
이 문제는 여전히 noexcept 이동 생성자에 적용됩니까? (물론 0 사본은 두 움직임이보다 효율적이지만, 즉 + 예외 안전을위한 문을, efficent 결합 전면을 열 수있는 팝업)
peppe

1
@peppe, value_typenothrow 이동 생성자가 있다는 것을 알고 있으면 값으로 반환 할 수 있지만 큐 인터페이스는 저장하는 객체 유형에 따라 다르므로 도움이되지 않습니다.
조나단 Wakely

1
@peppe : 안전하지만 스택은 일반 라이브러리 클래스이므로 요소 유형에 대해 더 많이 가정할수록 유용성이 떨어집니다.
Jon

나는 STL가 허용하지 않을 알고 있지만, 그 수단 중 하나는 블랙 잭과 자신의 큐 클래스를 구축하고 있습니다 ^ W ^ W ^ :이 기능 W
peppe

3

현재 구현에서는 유효합니다.

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;

1

C ++ 11부터 이동 시맨틱을 사용하여 원하는 동작을 아카이브 할 수 있습니다. 처럼 pop_and_move. 따라서 복사 생성자는 호출되지 않으며 성능은 이동 생성자에만 의존합니다.


2
아니요, 이동 의미론은 마술처럼 pop예외를 안전하게 만들지 않습니다 .
LF

0

완전히 할 수 있습니다.

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

또는 변수의 값을 원하면 참조를 사용하십시오.

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

그 다음으로 '더 현명한'이라는 표현은 '사용 패턴을 조사하고 분할이 더 필요함을 발견했습니다'의 주관적인 형태입니다. (안심하세요 : C ++ 언어는 가볍게 진화하지 않습니다 ...)


0

최선의 해결책은 다음과 같은 것을 추가하는 것입니다.

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

여기서 값은 팝된 값을받습니다.

장점은 이동 할당 연산자를 사용하여 구현할 수있는 반면 front + pop을 사용하면 복사본을 만들 수 있다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.