기능 템플릿 선언 순서는 가시성에 영향을 미칩니다 (때로는)
-
03-07-2019 - |
문제
나는 함수를 만들려고 노력하고있다 :
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
시청해야합니다.
호출이 잘못되었거나 더 나은 일치를 찾을 수있는 경우 관련 네임 스페이스 내에서 조회하여 템플릿 정의 및 템플릿에서 발견 된 해당 선언을 고려할뿐만 아니라 모든 번역 단위에 해당 네임 스페이스에 도입 된 모든 기능 선언을 고려했습니다. 인스턴스화 맥락에서, 프로그램은 정의되지 않은 동작을 가지고 있습니다.
따라서 작동하는 것은 정의되지 않은 행동 일 것입니다. 컴파일러가 그것을 받아들이는 것이 유효하지만, 그것을 거부하거나 충돌하고 종료하는 것이 유효합니다. 따라서 설명 된대로 인스턴스화 컨텍스트에서 필요한 이름이 실제로 보이는지 확인하십시오.
도움이 되었기를 바랍니다.