2010-08-18 3 views
10

나는이 작업을 명시 적으로 수행 할 방법이 없다고 확신하지만, 더 나은 방법이있을 경우를 대비하여 질문하고 싶습니다. 나는 기본 클래스 A와 파생 클래스 B를 가지고 있습니다. 이제는 B *를 가리키는 A * 목록을 가지고 있고이 A * 목록을 std :: B 벡터에 복사하려고합니다. * '의 그래서 기본적으로 나는이 작업을 수행 할 수 :C++ std :: 파생 클래스로 타입 캐스트가 가능한 복사가 가능합니까?

std::list<A*> aList = someObject.getAs(); 
std::vector<B*> bVec = std::vector<B*>(aList.begin(), aList.end()); 

내가 목록과 벡터는 같은 종류의 것이다 때 컴파일해야 확신합니다 (예 : 모두 A * 있었다의)하지만,이에 있기 때문에

std::list<A*> aList = someObject.getAs(); 
std::vector<B*> bVec; 
bVec.reserve(aList.size()); 
std::list<A*>::iterator it = aList.begin(); 
for(it; it!=aList.end(); ++it) 
{ 
    B* b = static_cast<B*>(*it); 
    bVec.push_back(b); 
} 

내 두 번째 방법 또는 것보다 더 우아한 방법이 있나요 : 내가 명시 적으로 다음과 같은 예를 들어 캐스트해야하기 때문에 경우 A *, 나는 이런 식으로 할 수없는 B의 *의 기본 클래스입니다 나는 그렇게해야만 해?

+0

을, dynamic_cast는이다 static_cast보다 안전합니다. static_cast는 B *가 아니더라도 B *를 제공하지만 dynamic_cast는 널 포인터를 제공합니다. 두 경우 모두 A *가 B를 가리 키지 않으면 B로 처리하면 정의되지 않은 동작이 발생하지만 dynamic_cast 버전에서는 적어도 B가 아닌지 알 수 있습니다. –

+2

@David Thornley : ... "A"가 적어도 하나의 가상 함수를 가지고있는 한. –

+0

@Charles : 고마워요. 잘 잡으세요. –

답변

15

변환을 암시 적으로 수행하는 것은 안전하지 않으므로 명시 적으로 지정해야합니다.

struct A {}; 
struct B : A {}; 

template <typename From, typename To> 
struct static_caster 
{ 
    To* operator()(From* p) {return static_cast<To*>(p);} 
}; 

std::list<A*> a; 
std::vector<B*> b; 
std::transform(a.begin(), a.end(), std::back_inserter(b), static_caster<A,B>()); 
+0

OP는이 IMHO와 함께 있어야합니다. –

+0

모든 대문자 식별자는 일반적으로 MACRO 용으로 예약되어 있습니다. – Puppy

+0

이것이 OP의 코드보다 더 우아하지는 않지만 STL 방법이라고 생각합니다. :) static_caster가 무엇인지 파악하기 위해 주변을 검색하는 것이 싫어합니다. – Andrew

7

캐스팅 할 펀터를 정의하십시오 (예 : 캐스팅).

struct Downcast 
{ 
    B* operator() (A* a) const 
    { 
     return static_cast< B* >(a); 
    } 
}; 

하고 대신 std::copy

bVec.resize(aList.size()); 
std::transform(aList.begin(), aList.end(), bVec.begin(), Downcast()); 

주의 std::transform를 사용 또한 bVec 필요에 따라 증가 할 경우

std::vector<B*> bVec; 
std::transform(aList.begin(), aList.end(), std::back_inserter(bVec), Downcast()); 

할 수 있지만 내가 할 첫 번째 방법을 선호 절대적으로 메모리 할당이 모두 한 번에 완료되었는지 확인하십시오. @Mike Seymour가 지적한대로 두 번째 경우에 bVec.reserve(aList.size())을 호출하여 하나의 할당을 보장 할 수 있습니다.

+0

Downcast generic을 만들 수도 있습니다. 동적 버전과 구별하기 위해 "static_downcast"라고해야합니다. 나중에 잘 작성해야 할 수도 있습니다. –

+0

@ 노아 로버츠 : 좋은 지적. – Troubadour

+0

'resize()'가 아니라'reserve()'여야합니다. 그 중 하나, 또는'bVec.begin()'대신'back_inserter (bVec)'를 사용하여 객체를 덮어 쓰지 않고 밀어 넣을 수 있습니다. –

1

반복기 어댑터 접근법을 사용할 수는 있지만 올바르게 수행하는 것이 좋습니다. 반복자를 "반복자"로 만드는 모든 요소를 ​​무시하거나 Boost.Iterator 라이브러리를 사용하여 이러한 작업을보다 쉽게 ​​수행 할 수 있습니다.

다른 방법으로는 펑터를 만들고 std :: copy 대신 std :: transform을 사용하면됩니다. 이것은 나에게 훨씬 쉬운 접근법으로 보일 것이다. C++ 0x 컴파일러를 사용한다면 람다를 사용할 수도 있습니다.

편집 : 어댑터 사용을 제안한 사람이 대답을 취소하므로 첫 번째 단락이 적합하지 않을 수 있습니다. A * 대신 B *를 반환하는 벡터 반복자 주위에 래퍼를 사용했지만 올바르게 수행하는 데 필요한 많은 작업을 생략했습니다.

2

이 변환 사용 : 시퀀스에 변화의 어떤 종류를 적용하기위한 표준 알고리즘은 다음과 같이 빈 용기를 채우는 데 사용할 수있는, std::transform이며이를 위해

#include <cstdlib> 
#include <vector> 
#include <algorithm> 
using namespace std; 

class A 
{ 
}; 
class B : public A 
{ 
}; 

A* get_a() { return new B; } 

B* make_b(A* a) { return static_cast<B*>(a); } 

int main() 
{ 
    vector<A*> a_list; 
    vector<B*> b_list; 

    generate_n(back_inserter(a_list), 10, get_a); 
    transform(a_list.begin(), a_list.end(), back_inserter(b_list), make_b); 

    return 0; 
} 
관련 문제