2016-12-08 13 views
6

큰 포인터를 원시 포인터 대신 사용자 지정 공유 포인터를 사용하도록 변환 중입니다. 과부하 해결에 문제가 있습니다. 이 예제를 고려하십시오 qualification conversions는 암시 적 변환 시퀀스의 순위에 중요한 역할을하기 때문에오버로드 해결 및 const3200에 대한 공유 포인터

#include <iostream> 

struct A {}; 

struct B : public A {}; 

void f(const A*) 
{ 
    std::cout << "const version\n"; 
} 

void f(A*) 
{ 
    std::cout << "non-const version\n"; 
} 

int main(int, char**) 
{ 
    B* b; 
    f(b); 
} 

이 코드는 제대로 "const가 아닌 버전"을 씁니다. 이제 shared_ptr을 사용하여 버전을 살펴보십시오.

#include <iostream> 
#include<memory> 

struct A {}; 

struct B : public A {}; 

void f(std::shared_ptr<const A>) 
{ 
    std::cout << "const version\n"; 
} 

void f(std::shared_ptr<A>) 
{ 
    std::cout << "non-const version\n"; 
} 

int main(int, char**) 
{ 
    std::shared_ptr<B> b; 
    f(b); 
} 

이 코드는 함수 호출이 모호하기 때문에 컴파일되지 않습니다.

user-defined deduction-guide은 해결책이 될 수 있지만 여전히 Visual Studio에는 존재하지 않는다는 것을 알고 있습니다.

수천 개의 호출이 있기 때문에 regexp를 사용하여 코드를 변환하고 있습니다. 정규 표현식은 const 버전과 일치하는 호출을 비 const 버전과 일치하는 호출과 구별 할 수 없습니다. 공유 포인터를 사용할 때 과부하 해결을보다 세밀하게 제어 할 수 있으며 수동으로 각 호출을 변경하지 않아도됩니까? 물론 원시 포인터를 .get() 할 수 있고 호출에서 사용하지만 원시 포인터를 모두 없애고 싶습니다.

+0

@ πάνταῥεῖ 무료 함수 오버로드입니다. 멤버 함수가 전혀 포함되어 있지 않습니다. –

+0

"물론 원시 포인터를 가져 와서 호출에서 사용할 수 있지만 원시 포인터를 모두 없애려고합니다." 원시 포인터를 높이는 것이 반드시 최선의 행동 과정은 아닙니다. 많은 경우, 컨테이너가 해당 인스턴스에 대한 메모리 액세스로 지역을 감소시키기 때문에 원시 포인터는 'shared_ptr'보다 훨씬 더 성능이 좋습니다. 시간이 있다면 [Herb Sutter의 스마트 포인터 매개 변수 게시] (https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/)를 확인하시기 바랍니다. – cyberbisson

+1

@cyberbisson 당신은 당연한 일입니다. 그러나 많은 사람들이 원시 포인터를 사용하여 많은 사람들이 지나가는 혼란 상태에서 코드를 구하기 위해 공유 포인터가 필요합니다. 그리고 일부 주문에 대해서는 적은 비용을 지불 할 용의가 있습니다. 링크를 주셔서 감사합니다, 나는 언제든지 상수 참고로 공유 포인터를 전달합니다. –

답변

1

당신은 당신을 위해 delagation을 추가 과부하가 들어갈 수 있습니다 : 당신은 잠재적으로 T의 모두에 과부하를 constT들 비 -하는 최초의 과부하를 제한 할 std::enable_if를 사용, 및/또는 제한 할 수 있습니다

template <class T> 
void f(std::shared_ptr<T> a) 
{ 
    f(std::static_pointer_cast<A>(a)); 
} 

template <class T> 
void f(std::shared_ptr<const T> a) 
{ 
    f(std::static_pointer_cast<const A>(a)); 
} 

A에서 파생되었습니다. 이 작품은 어떻게

:

당신은 몇 가지입니다 XAconst A에 대한 std::shared_ptr<X>을 (이 중 하나 B 또는 const B이다)이있다. 템플릿 오버로드가 없으면 컴파일러는 std::shared_ptr<X>std::shared_ptr<A> 또는 std::shared_ptr<const A>으로 변환하도록 선택해야합니다. 둘 다 순위가 똑같은 훌륭한 전환이며 (둘 다 사용자 정의 전환 임) 모호성이 있습니다.

추가 템플릿 과부하로

선택할 수있는 네 가지 매개 변수 유형이있다 (의가 X = const B 케이스를 분석 할) :

  1. std::shared_ptr<A>
  2. std::shared_ptr<const A>
  3. std::shared_ptr<const B>

    T = const B와 함께, 최초의 템플릿 인스턴스화.
  4. std::shared_ptr<const B>T = B으로 제 2 주형으로부터 착취된다.

분명히 유형 3과 4는 변환을 전혀 필요로하지 않으므로 유형 1과 2보다 우수합니다. 그러므로 그들 중 하나가 선택 될 것입니다.

유형 3과 4는 그 자체로 동일하지만 템플리트의 과부하 해결과 함께 추가 규칙이 제공됩니다. 즉, "보다 특수화 된"(더 많은 비 템플릿 서명 일치) 템플리트가 덜 전문화되어 있습니다. 오버로드 4는 const이 (T 외부의) 서명의 비 템플릿 부분에 있었기 때문에 더욱 전문화되어 선택됩니다.

"템플릿이 더 좋습니다"라는 규칙은 없습니다. 사실, 반대입니다. 템플릿과 비 템플릿의 비용이 같을 때 템플릿이 아닌 것이 좋습니다. 여기서 트릭은 보다 작은 템플릿 (변환 필요 없음)이 템플릿이 아닌 것 (사용자 정의 변환 필요)보다 크다는 것입니다.

+0

작동하지만 이해가되지 않습니다 ... 우리에게는 동일한 두 가지 기능과 두 가지가 더 있습니다. 템플릿 함수가 템플릿 이외의 함수보다 더 적합하다는 규칙이 있습니까? 더 읽을 거리를 가르쳐 주시겠습니까? –

+0

이제 알겠습니다. 감사! –

0

태그 디스패치으로 문제를 해결할 수 있습니다. 저장된 포인터 : 당신이 볼 수 있듯이, 당신은 이미 당신이 당신의 태그를 만들 필요가

#include <iostream> 
#include<memory> 

struct A {}; 

struct B : public A {}; 

void f(const A*, std::shared_ptr<const A>) 
{ 
    std::cout << "const version\n"; 
} 

void f(A*, std::shared_ptr<A>) 
{ 
    std::cout << "non-const version\n"; 
} 

template<typename T> 
void f(std::shared_ptr<T> ptr) 
{ 
    f(ptr.get(), ptr); 
} 

int main(int, char**) 
{ 
    std::shared_ptr<B> b; 
    f(b); 
} 

:
그것은 최소한의 근로 예를 들어 다음과 같습니다.
어쨌든, 그것을 얻지 않고 콜 지점에서 전달할 필요가 없습니다. 대신 중간 함수 템플릿을 사용하여 호출을 내부적으로 파견하는 유형으로 사용할 수 있습니다. 매개 변수를 사용하지 않으려는 경우 매개 변수의 이름을 지정할 필요조차 없습니다.