문제

나는 함수를 만들려고 노력하고있다 :

template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}

행동이 유형에 따라 변하는 곳 p 통과. 특히, 버전 getClassName 호출은 유형에 따라야합니다 p. 다음 예에서는 성공적으로 호출 할 수 있습니다.

doIt<myClass1>( myClass1*& )
doIt<myClass1<int> >( myClass1*& )
doIt<myClass2>( myClass2*& )
doIt<myClass2<int> >( myClass2*& )

그러나 내가 전화 할 때 실패합니다.

doIt< std::vector<int, std::allocator<int> > >( std::vector<int, std::allocator<int>>*& )

오류로 :

a.cxx: In function ‘void doIt(T*&) [with T = std::vector<int, std::allocator<int> >]’:
ba.cxx:87:   instantiated from here
a.cxx:33: error: invalid initialization of reference of type ‘MyClass1&’ from expression of type ‘std::vector<int, std::allocator<int> >’
a.cxx:16: error: in passing argument 1 of ‘const char* getClassName(MyClass1&)’

(GCC 4.2.4).

선언을 옮기면 :

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

doit 전에 - 컴파일됩니다. 그래서,

  • 왜 필요한가? getClassName( std::vector<T,A>& ) 전에 나타납니다 doIt 하지만 getClassName( MyClass2T<T>& )
  • 무엇을 만들 수 있습니까? doIt 독립적 std::vector? (나는 배치 할 수 있기를 원한다 doIt 자체 헤더에서 알 필요가 없습니다. std::vector, 또는 사용자 정의 될 전문화).

.

#include <stdio.h>
#include <assert.h>
#include <vector>

//template<typename T>
//char const* getClassName( T& );

//template<typename T, typename A>
////char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

#if 1
// ---------  MyClass2
struct MyClass1
{};

char const* getClassName( MyClass1& ) { printf("MyClass1\n"); return NULL; }

// ---------  MyClass1T
template< typename T>
struct MyClass1T
{};

template<typename T>
char const* getClassName( MyClass1T<T>& ) { printf("MyClass1T<T>\n"); return NULL; }
#endif


template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}


// ---------  MyClass2
struct MyClass2
{};


// declared after doIt, OK.
char const* getClassName( MyClass2& ) { printf("MyClass2\n"); return NULL; }

// ---------  MyClass2T
template< typename T>
struct MyClass2T
{};

// declared after doIt, OK.
template<typename T>
char const* getClassName( MyClass2T<T>& ) { printf("MyClass2T<T>\n"); return NULL; }

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }



void test()
{
#if 1
   MyClass1 mc1;
   MyClass1* mc1p = &mc1;
   doIt( mc1p );

   MyClass2 mc2;
   MyClass2* mc2p = &mc2;
   doIt( mc2p );

   MyClass1T<int> mc1t;
   MyClass1T<int>* mc1tp = &mc1t;
   doIt( mc1tp );

   MyClass2T<int> mc2t;
   MyClass2T<int>* mc2tp = &mc2t;
   doIt( mc2tp );

   // Nested templates are OK.
   MyClass2T<MyClass1> mc2t2;
   MyClass2T<MyClass1>* mc2tp2 = &mc2t2;
   doIt( mc2tp2 );
#endif

#if 1
   std::vector<int> v;
   std::vector<int>* vp = &v;
   doIt( vp );                   // FAIL!
#endif
}
도움이 되었습니까?

해결책

getClassName (std :: vector &)이 doit 전에 나타나지 만 getClassName (myclass2t &)이 필요하지 않은 이유는 무엇입니까?

모든 기능에 범위가 선언되어야합니다. 템플릿 기능을 a로 인스턴스화 할 때 vector<int> 서명이있는 함수가 예상됩니다 getClassName(vector<int>&) 컴파일이 성공하기위한 존재 (적어도 프로토 타입).

std :: 벡터와 독립적으로 Doit을 할 수 있습니까? (나는 자체 헤더에 doit을 배치 할 수 있고 std :: 벡터 또는 사용자 정의 될 전문화에 대해 알 필요가 없다.

읽기 템플릿의 FAQ. 모든 프로토 타입을 넣으십시오 doIt첫 번째 인스턴스화 전 종속 템플릿 함수 doIt.

다른 팁

실패의 이유는 인스턴스화에서 기능에 대한 자격이없는 이름 조회가 발생하지 않기 때문입니다 (그러나 ADL- 인수 종속 조회 만). 인스턴스화 컨텍스트는 (에서 가져 왔습니다 14.6.4.1/6 C ++ 표준) :

템플릿 인수에 의존하는 표현식의 인스턴스화 컨텍스트는 동일한 번역 단위에서 템플릿 전문화를 인스턴스화하기 전에 외부 연결이 선언 된 선언 세트입니다.

이 경우에 전화 한 모든 템플릿 전문화의 인스턴스화 요점은의 정의 직후입니다. test (읽다 14.6.4.1/1). 따라서 선언 한 모든 기능은 귀하의 test 자격이없는 조회를 사용하는 기능이지만 실제로 조회는 기능 호출에 대해 다릅니다.

템플릿 내의 템플릿 매개 변수에 따라 다루는 함수 호출은 다음과 같습니다.

  • 템플릿 정의 컨텍스트의 이름은 일반적인 조회와 ADL로 고려됩니다.
  • 인스턴스화 컨텍스트의 이름은 ADL에 대해서만 고려됩니다.

이것은 적합하지 않기 때문에 의미합니다 getClassName 템플릿의 정의 컨텍스트에서 선언 된 함수는 ADL을 사용하여 인스턴스화 컨텍스트에서 적절한 기능을 찾아야합니다. 그렇지 않으면 호출이 실패하고 선언을 찾지 못합니다.

인수 종속 조회 (ADL)

유형의 주장을 위해 std::vector<T>, ADL은 네임 스페이스에서 함수를 검색합니다 std 그리고 네임 스페이스 T. put getClassName function into the std 네임 스페이스는 이것에 대해 작동하지만 (그러나 그렇게하는 것은 정의되지 않은 동작으로의 수율이기 때문에 표준에 의해 허용되지 않습니다. 이것은 최후의 수단으로 만 수행되어야합니다).

의 효과를보기 위해 ADL 전화를 시도하십시오 doIt 벡터와 함께 MyClass2 대신에 int. 그때부터 T = MyClass2, ADL은 네임 스페이스에서 검색합니다 MyClass2 적절한 기능을 수락하는 경우 a std::vector<MyClass2> 그리고 성공할 것입니다 - 당신이 사용할 때에 반대합니다 int, 그것은 단지 조사 할 것입니다 std.

다른 함수 호출의 경우, 각각의 선언은 모두 글로벌 네임 스페이스에서 선언되기 때문에 모두 발견되기 때문에 함수 호출의 인수 유형도 정의됩니다 (MyClass1, MyClass2 등).

C ++ FAQ는 양호하지만 템플릿에 깊이 들어 가지 않습니다 (ADL에 대한 언급은 찾지 못했습니다). 전용이 있습니다 템플릿 FAQ 그것은 더 복잡한 함정을 처리합니다.


정의되지 않은 행동을 조심하십시오

내가 보여준 선언을 넣을 때에도 많은 컴파일러가 코드를 수락합니다. ~ 후에 그만큼 test 기능 (이전 대신). 그러나 위의 표준 인용문에서 알 수 있듯이, 선언은 인스턴스화 컨텍스트와 발견 된 규칙의 일부가되지 않습니다. 14.6.4.2/1 시청해야합니다.

호출이 잘못되었거나 더 나은 일치를 찾을 수있는 경우 관련 네임 스페이스 내에서 조회하여 템플릿 정의 및 템플릿에서 발견 된 해당 선언을 고려할뿐만 아니라 모든 번역 단위에 해당 네임 스페이스에 도입 된 모든 기능 선언을 고려했습니다. 인스턴스화 맥락에서, 프로그램은 정의되지 않은 동작을 가지고 있습니다.

따라서 작동하는 것은 정의되지 않은 행동 일 것입니다. 컴파일러가 그것을 받아들이는 것이 유효하지만, 그것을 거부하거나 충돌하고 종료하는 것이 유효합니다. 따라서 설명 된대로 인스턴스화 컨텍스트에서 필요한 이름이 실제로 보이는지 확인하십시오.

도움이 되었기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top