2013-11-01 3 views
3

Java에서 Deque 클래스는 실제로 반환 된 요소를 반환하는 끝 부분에 대해 removal methods을가집니다. C++에서 동일한 동작을 얻는 유일한 방법은 먼저 명시 적으로 요소를 복사 한 다음 팝업하는 것입니다.C++ std :: vector 또는 std :: deque에서 동시에 객체 복사본을 제거하고 가져올 수 있습니까?

std::deque<int> myDeque; 
myDeque.push_back(5); 

int element = myDeque.back(); 
myDeque.pop_back(); 

두 가지를 동시에 수행 할 수있는 메커니즘이 있습니까?

당신은 당신의 자신의 래퍼 함수 템플릿을 작성할 수 있습니다
+0

답변으로 [java]를 제거하는 것은 Java와 관련이 없습니다. –

답변

11

:

// Precondition: !container.empty() 
// Exception safety: If there is an exception during the construction of val, 
//     the container is not changed. 
//     If there is an exception during the return of the value, 
//     the value is lost. 
template <typename C> 
auto back_popper(C & container) -> decltype(container.back()) 
{ 
    auto val(std::move(container.back())); 
    container.pop_back(); 
    return val; 
} 

사용법 :

auto element = back_popper(myDeque); 

당신은 처음에 back()pop_back()의 분리 동기를 부여 근본적인 문제를 해결받을 수없는 이 요소의 복사 또는 이동 생성자가 예외를 throw 할 수 있으며이 경우 팝업 된 요소가 손실 될 수 있습니다. 던져지지 않는 객체를 반환하여 사례별로 완화 할 수 있습니다. a unique_ptr, 이는 동적 할당을 위해 요소를 잃을 위험을 상쇄하지만, 분명히 표준이 당신을 위해 만들지 않는 개인 선택입니다. 예를 들어

:

// Guarantees that you either get the last element or that the container 
// is not changed. 
// 
template <typename C> 
auto expensive_but_lossless_popper(C & container) 
-> typename std::unique_ptr<decltype(container.back())> 
{ 
    using T = decltype(container.back()); 

    std::unique_ptr<T> p(new T(std::move(container.back()))); 
    container.pop_back(); 
    return p;    // noexcept-guaranteed 
} 

편집 : @Simple이 제안가 나는 back() 호출 주위 std::move을 추가했다. 이것은 이동 된 요소가 더 이상 필요하지 않기 때문에 합법적입니다. 많은 실제 클래스에는 noexcept 이동 생성자가 있으므로 많은 경우를 다루며 "무손실"해결 방법은 소수의 noexcept 움직임이없는 "이상한"유형. 디자인으로

+2

'back_popper'라는 이름은 IMO 함수보다 클래스처럼 들립니다. – Simple

+0

내가 대답을 게시 한 후 편집 내용을보고 코드와 설명으로 설명했습니다. +1 –

+0

@DanielFrey : 나는 더 나아가서 다른 해결책을 설명했습니다. -S –

10

, C++은 예외 안전 보장하기 위해 상자 밖으로 이러한 메커니즘을 제공하지 않습니다 : 다음, 당신이 그것을 구현하려고 할 때 먼저 요소의 복사본을 만들

을 컨테이너를 팝업 마침내 호출자에게 객체를 반환하려고합니다. 하지만 마지막 작업의 복사 생성자가 예외를 throw하면 어떻게됩니까? 개체가 더 이상 컨테이너에 없으며 호출자가 개체를받지 못했습니다. 이를 방지하고 컨테이너의 조작에 대한 강력한 예외 보장을 제공하기 위해 동시에 팝되는 요소를 리턴하는 조작은 직접적으로 보류되지 않습니다.

// 
// Precondition: !container.empty() 
//    
template <typename C> 
auto back_popper(C & container) 
-> typename std::enable_if<std::is_nothrow_move_constructible< 
          decltype(container.back())>::value, 
          decltype(container.back())>::type 
{ 
    auto val = std::move(container.back()); 
    container.pop_back(); 
    return val; 
} 

또는 static_assert이 (자신을 위해 그것을 해결) : 당신은 예외 안전을 느슨하게하지 않으려면

+0

C++ 11에서는 복사 생성시에 던지지 않기로 약속 한 객체를 식별 할 방법이 없습니까? 그런 다음 라이브러리는 이러한 요소 유형에 대해 copy-pop-and-return 메서드를 제공 할 수 있습니다. – Walter

+0

@Walter 이론 상으로는 'std :: is_nothrow_copy_constructible :: value'에 기반한'pop_back'에 대한 반환 형식을'void'에서'T'로 변경할 수 있습니다. 아니면 한 걸음 더 나아가 이동 작업과'std :: is_nothrow_move_constructible :: value'을 사용할 수 있습니다. 그러나 당신이 볼 수 있듯이, 고려해야 할 항목의 수는 (복사 가능 및/또는 이동 가능한 유형입니까?) 사소한 것이 아니며 AFAIK는 아직 제안 할 논문을 작성하지 않았습니다. –

+0

@Walter Anthony Williams의 [C++ Concurrency in Action] (http://www.amazon.com/C-Concurrency-Action-Practical) 섹션 3.2.3의 스레드 안전성 관점에서 제안 사항에 대해 자세히 설명합니다. -Multithreading/dp/1933988770). – TemplateRex

4

, 당신은 SFINAE를 사용하여이 방법을 제한 할 수 있습니다. (참고 : 이동 생성자가있는 경우 복제본을 피하기 위해 편집 됨 Simple의 설명을 따르십시오.)

+0

이것은 아마도'std :: is_nothrow_move_constructible'이어야합니다. – Simple

+0

@Simple 예, 그렇지만 움직이지 않을 수도 있고 여전히 복사 할 수 없을 수도 있습니다 ... 모든 우발적 사건에 대해 허용됩니다 (제 생각에) 편집을 참조하십시오. – Walter

+0

타입이 움직일 수 없다면'std :: is_nothrow_move_constructible'는 그것이 어떻게 정의 되었는가에 따라 생성 가능한 복사본이 아닌지 검사 할 것입니다. 'return std :: move (val) '은 복사 제거를 방지합니다. 'return val' 만 써야합니다. – Simple