2013-06-19 1 views
3

Visual Studio 2012 C++을 사용하고 있으며 고유 포인터가 서로 다른 두 벡터를 설정하려고합니다.두 개의 벡터 <unique_ptr <...>>을 서로 어떻게 설정합니까?

using namespace std; 
    vector<unique_ptr<Unit>> unitVector; 
    vector<unique_ptr<Unit>> nonDeadUnits; 

    .... (stuff added to unitVector) .... 

    for (auto unit = unitVector.begin(); unit != unitVector.end(); ++unit) { 
      if ((*unit)->health > 0) { 
        nonDeadUnits.push_back(*unit); 
      } 
    } 

    unitVector.clear(); 
    unitVector = nonDeadUnits; // error here (see below error code) 

내가 덜이 0보다 건강이있는 모든 유닛을 제거 할, 그러나 나는 시도하고 직접 벡터에서 제거하는 경우, 나는이 프로그램을 죽이는 안 메모리에 액세스 할 수. 그래서 내가 이런 방식으로 선택했다. 유일한 문제는 unique_ptr이 원하는 복사 유형을 허용하지 않는다는 것입니다. 여기에 오류가 : 나는 벡터 나중에 for 루프 내에서 하위 클래스 메소드를 호출하기 때문에 unique_ptr의의를하고 싶은

error C2248: 'std::unique_ptr<_Ty>::operator =' : cannot access private member declared in class 'std::unique_ptr<_Ty>' c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility 2089 

, 그리고 최우선으로하는 데 도움이됩니다. 그렇다면 벡터를 서로 같게 설정하거나 더 좋은 방법이 있습니까?

+3

'std :: remove_if'가 당신 친구일지도 모른다고 생각합니다. 예제를 작성하여 답변으로 게시하려고 노력할 것입니다. – Quuxplusone

+0

'unitVector = std :: move (nonDeadUnits);'는 문제를 해결할 것이지만 Quuxplusone이 말한 것처럼 더 나은 해결책이 있습니다. –

+4

"unique"ptr ..... –

답변

8

일반적으로 std::remove_if을 사용하여 unitsVector 내에있는 요소를 교환 한 다음 모든 죽은 유닛이 벡터 끝에 도달하면 해당 요소를 잘라냅니다.

#include <memory> 
#include <vector> 

struct Unit { 
    int health; 
}; 

// The non-working version. 
// 
// void remove_dead_units(std::vector<std::unique_ptr<Unit>> &unitVector) 
// { 
//  std::vector<std::unique_ptr<Unit>> nonDeadUnits; 
//  for (auto unit : unitVector) 
//   if (unit->health > 0) 
//    nonDeadUnits.push_back(unit); 
//  unitVector = nonDeadUnits; 
// } 

void remove_dead_units(std::vector<std::unique_ptr<Unit>> &unitVector) 
{ 
    auto isDead = [](const std::unique_ptr<Unit> &u) -> bool { return (u->health <= 0); }; 
    auto newEnd = std::remove_if(unitVector.begin(), unitVector.end(), isDead); 
    unitVector.erase(newEnd, unitVector.end()); 
} 

난 당신이 뭘하려에 더 밀접하게 hewing 그것을 할 다른 방법이 있습니다 확신 (편집 : 사실 KerrekSB 그냥 단일 std::moveswap를 사용하여, 하나의 게시가); 그러나 나는 "셔플과 "핑"방법이 더 현대적이라고 생각합니다.-C++ ish.

5

아마도 다음과 같은 논리는 간단 할 것 :

unitVector.erase(remove_if(unitVector.begin(), unitVector.end(), 
          [](unique_ptr<Unit> const & p) -> bool { return p->health <= 0; }), 
       unitVector.end()); 
+0

왜 '스왑'하고 '이동'하지 않습니까? – Yakk

1

이 작업을 수행하는 빠른 방법은 remove_if을 함께 :

vector<unique_ptr<Unit>> unitVector = /* ... */; 
vector<unique_ptr<Unit>> nonDeadUnits; 

for (auto & p : unitvector) 
{ 
    if (p->health > 0) { nonDeadUnits.push_back(std::move(p)); } 
} 

unitVector.swap(nonDeadUnits); 

그렇지 않으면, 표준 제거 지우기 관용구는 아마 더 메인 스트림 그리고 erase이 숙어는 DRY에 위배됩니다. (반복해서 말하지 마십시오.) 사람들은 그것을 사용할 때 미묘한 실수를하는 것을 보았습니다. erase 두 번째 반복자를 잊어 버렸을 때 (부적절한) 테스트 케이스를 통과 한 다음 생산에 실패했습니다.)

일부 속성에 대해 std::vector을 필터링하는 것과 같은 이런 종류의 문제에 대한 해결책은 나를 위해 컨테이너 기반 알고리즘을 작성하는 것입니다.

template<typename SeqContainer, typename Lambda> 
SeqContainer&& remove_erase_if(SeqContainer&& c, Lambda&& test) { 
    using std::begin; using std::end; 
    auto new_end = std::remove_if(begin(c), end(c), std::forward<Lambda>(test)); 
    c.erase(new_end, end(c)); 
    return std::forward<SeqContainer>(c); 
} 

이제 remove_erase_if을 기반으로 컨테이너를 가지고, 우리는 목록을 필터링 할 수 있습니다

// const & is important, we don't want to copy a `unique_ptr` 
remove_erase_if(unitVector, [&](std::unique_ptr<Unit> const& unit) { 
    return (unit->health() <= 0); 
}); 

을 ... 그리고 그게이다. health() <= 0의 모든 내용이 std::vector에서 제거됩니다.

다른 유용한 컨테이너 기반 알고리즘은 내가 자주 사용하는 것을 발견했습니다. remove_erasesort_unique_erasebinary_search을 자주 사용합니다. 놀랍게도 위의 코드는 std::vector, std::liststd::deque으로 작동하지만 거의 항상 std::vector을 사용합니다. 그러나 순차 컨테이너로 작동하려면 std::vector으로 작업하는 것보다 쓰기가 쉽습니다.

이러한 컨테이너 알고리즘을 설계하는 또 다른 옵션은 값으로 컨테이너를 가져 와서 값으로 반환하는 것입니다.이는 일부 std::move 스팸을 강제하지만 기본적으로 런타임시 효율적입니다.

관련 문제