shared_ptr <Derived>를 shared_ptr <Base>로 전달


93

shared_ptr파생 유형을 shared_ptr기본 유형 을 취하는 함수 에 전달하는 가장 좋은 방법은 무엇입니까 ?

나는 일반적으로 shared_ptr불필요한 사본을 피하기 위해 s를 참조로 전달합니다 .

int foo(const shared_ptr<bar>& ptr);

하지만 다음과 같은 작업을 시도하면 작동하지 않습니다.

int foo(const shared_ptr<Base>& ptr);

...

shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);

나는 사용할 수있다

foo(dynamic_pointer_cast<Base, Derived>(bar));

그러나 이것은 두 가지 이유로 차선책으로 보입니다.

  • A dynamic_cast는 단순한 파생에서 기본 캐스트에 대해 약간 과도하게 보입니다.
  • 내가 이해했듯이 dynamic_pointer_cast함수에 전달할 포인터의 복사본 (임시적이긴하지만)을 만듭니다.

더 나은 해결책이 있습니까?

후손을위한 업데이트 :

누락 된 헤더 파일 문제로 판명되었습니다. 또한 여기서 제가하려는 것은 반 패턴으로 간주됩니다. 일반적으로,

  • 객체의 수명에 영향을주지 않는 함수 (즉, 객체가 함수 기간 동안 유효 함)는 일반 참조 또는 포인터를 가져야합니다 (예 : int foo(bar& b).

  • 객체 를 소비 하는 함수 (즉, 주어진 객체의 최종 사용자)는 unique_ptr값 (예 :)을 가져야합니다 int foo(unique_ptr<bar> b). 호출자는 std::move함수에 값을 입력해야합니다.

  • 객체의 수명을 연장하는 함수는 shared_ptr예를 들어 int foo(shared_ptr<bar> b). 순환 참조 를 피하기위한 일반적인 조언이 적용됩니다.

자세한 내용은 Herb Sutter의 Back to Basics 토크 를 참조하십시오.


8
통과하려는 이유는 무엇 shared_ptr입니까? bar의 const 참조가없는 이유는 무엇입니까?
ipc 2011

2
모든 dynamic캐스트는 다운 캐스팅에만 필요합니다. 또한 파생 된 포인터를 전달하면 잘 작동합니다. shared_ptr동일한 refcount (및 증가)와 기본에 대한 포인터를 가진 새 를 생성 한 다음 const 참조에 바인딩합니다. 그러나 이미 참조를 받고 있기 때문에 왜 그것을 받고 싶은지 전혀 모르겠습니다 shared_ptr. 받아 Base const&전화 foo(*bar).
Xeo

@Xeo : 파생 포인터를 전달 (예 foo(bar))하지 적어도 MSVC 2010 년하지 작업,
매트 클라인

1
"분명히 작동하지 않는다"는 의미는 무엇입니까? 코드가 올바르게 컴파일되고 작동합니다. shared_ptr함수에 전달할 임시 생성을 피하는 방법을 묻고 있습니까? 나는 그것을 피할 방법이 없다고 확신합니다.
Mike Seymour

1
@Seth : 동의하지 않습니다. 공유 포인터를 값으로 전달할 이유가 있고 참조로 공유 포인터를 전달할 이유가 거의 없다고 생각합니다. 여기에서 추론 stackoverflow.com/questions/10826541/…
R. Martinho Fernandes

답변:


47

BaseDerived공변 이더라도 그들에 대한 원시 포인터는 그에 따라 작동 shared_ptr<Base>하며 공변 shared_ptr<Derived>아닙니다 . 는 dynamic_pointer_cast이 문제를 처리 할 수있는 정확하고 간단한 방법입니다.

( 편집 : static_pointer_cast 파생에서 기본으로 캐스팅하므로 안전하고 런타임 검사가 필요하지 않으므로 더 적합합니다. 아래 주석을 참조하십시오.)

그러나 foo()함수가 수명 연장에 참여하지 않으려는 경우 (또는 객체의 공유 소유권에 참여하는 대신) 를 전달할 때 를 수락 const Base&하고 역 참조하는 것이 가장 좋습니다 .shared_ptrfoo()

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);

제쳐두고, shared_ptr유형은 공변이 될 수 없기 때문에 공변 반환 유형에 대한 암시 적 변환 규칙은의 유형을 반환 할 때 적용되지 않습니다 shared_ptr<T>.


39
그들은 공변이 아니지만 shared_ptr<Derived>암시 적으로로 변환 할 shared_ptr<Base>수 있으므로 코드는 캐스팅 허위없이 작동해야합니다.
Mike Seymour

9
음, shared_ptr<Ty>를 묵시적으로 변환 할 수있는 shared_ptr<Other>경우 적절한 변환 을 수행하는 생성자가 있습니다. 캐스팅이 필요한 경우에, 여기에 적절한 일이 아니다 . Ty*Other*static_pointer_castdynamic_pointer_cast
Pete Becker

사실이지만 질문에서와 같이 참조 매개 변수가 아닙니다. 그는 상관없이 복사를해야합니다. 그러나 그가 shared_ptr참조 횟수를 피하기 위해 's에 대한 refs를 사용한다면 shared_ptr, 처음부터 a를 사용할 이유가 없습니다 . const Base&대신 사용하는 것이 가장 좋습니다.
Bret Kuhns 2011

@PeteBecker 변환 생성자에 대한 Mike의 의견을 참조하십시오. 솔직히 static_pointer_cast, 감사합니다.
Bret Kuhns 2011

1
@TanveerBadar 확실하지 않습니다. 2012 년에 컴파일하지 못했을까요? (특히 Visual Studio 2010 또는 2012 사용). 그러나 당신 말이 맞습니다. / publicly derived / 클래스의 전체 정의가 컴파일러에 표시되면 OP의 코드는 절대적으로 컴파일되어야합니다.
Bret Kuhns

32

파생 클래스에 대해 공용 상속 을 지정하는 것을 잊은 경우에도 마찬가지입니다. 즉, 저처럼 다음과 같이 작성합니다.

class Derived : Base
{
};

class템플릿 매개 변수 용입니다. struct클래스를 정의하기위한 것입니다. (이것은 대부분의 45 %에 농담입니다.)
데이비스 청어

이것은 분명히 해결책으로 간주되어야하며 공개 만 누락되기 때문에 캐스트가 필요하지 않습니다.
Alexis Paques

12

너무 열심히 노력하는 것 같습니다. shared_ptr복사하기가 저렴합니다. 그것이 목표 중 하나입니다. 참조로 전달하는 것은 실제로 많은 것을 성취하지 못합니다. 공유하지 않으려면 원시 포인터를 전달하십시오.

즉, 머리 위에서 생각할 수있는 두 가지 방법이 있습니다.

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));

9
아니요, 복사 비용이 저렴하지 않으며 가능할 때마다 참조로 전달해야합니다.
Seth Carnegie

6
@SethCarnegie-Herb가 가치 전달이 병목인지 확인하기 위해 코드를 프로파일 링했습니까?
Pete Becker

25
@SethCarnegie-내가 묻는 질문에 대답하지 않습니다. 그리고 그 가치를 위해 shared_ptrMicrosoft에서 제공 하는 구현을 작성했습니다 .
Pete Becker

6
@SethCarnegie-당신은 휴리스틱을 거꾸로 가지고 있습니다. 수동 최적화는 일반적으로 필요함을 보여줄 수 없는 경우 수행 하지 않아야 합니다.
Pete Becker

21
작업해야하는 경우 "조기"최적화 일뿐입니다. 특정 상황에서 차이가 있든 없든 비효율적 인 관용구보다 효율적인 관용구를 채택하는 데 문제가 없다고 생각합니다.
Mark Ransom

11

또한 #include파생 클래스의 전체 선언을 포함하는 헤더 파일의가 소스 파일에 있는지 확인하십시오.

나는이 문제가 있었다. 은 std::shared<derived>에 캐스팅 않을 것이다 std::shared<base>. 나는 그들에 대한 포인터를 보유 할 수 있도록 두 클래스를 앞으로 선언했지만 #include컴파일러가 없었기 때문에 한 클래스가 다른 클래스에서 파생되었음을 알 수 없었습니다.


1
와우, 예상하지 못했지만 이로 인해 해결되었습니다. 나는 특별히 조심했고 내가 필요한 곳에 헤더 파일 만 포함 시켰기 때문에 그들 중 일부는 소스 파일에만 있었고 당신이 말한 것처럼 헤더에서 그들을 앞으로 선언했습니다.
jigglypuff

멍청한 컴파일러는 멍청합니다. 이것은 내 문제였습니다. 감사합니다!
Tanveer Badar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.