2009-10-12 5 views
48

헤더에 평범한 (템플릿이 아닌) 클래스의 정적 변수를 정의하면 링커 오류가 발생하지만 템플릿의 경우 모두 정상적으로 작동하며 더 나아가 우리는 모든 번역 단위 사이의 정적 변수는 :템플릿 고정 변수

이 템플릿 헤더 (template.h)입니다 :

// template.h 
template<typename T> 
class Templ { 
public: 
    static int templStatic; 
}; 

template<typename T> Templ<T>::templStatic = 0; 

그것은 최초의 장치 템플릿을 사용하여 (unit1.cpp)

// unit1.cpp 
#include "template.h" 

int method1() { 
    return Templ<void>::templStatic++; 
} 

두 번째 단위 그는 재 (unit2.cpp) :

// unit2.cpp 
#include "template.h" 
int method2() { 
    return Templ<void>::templStatic++; 
} 

그리고 마지막으로, MAIN.CPP :

0 
1 
:

// main.cpp 
#include <iostream> 
int method1(); 
int method2(); 

int main(int argc, char** argv) { 
    std::cout << method1() << std::endl; 
    std::cout << method2() << std::endl; 
} 

, compilling 연결하고이 코드를 실행 한 후에, 우리는 출력이 다음과 같은 것이다

그럼 왜 템플릿의 경우 모두 정상적으로 작동합니까? 컴파일러 나 링커가 어떻게 처리합니까? (컴파일러를 분리하여 호출 할 때 각 .cpp 파일을 컴파일 한 다음 링커에 연결하여 컴파일러와 링커가 동시에 모든 .cpp 파일을 "보지"마십시오.)

PS : 제 컴파일러 : msvcpp 9 (하지만 너무와 Mingw에 확인)

+0

** ** 작동하지 않는 코드를 사용하면 더 유용 할 것입니다. – JesperE

+0

작동하지 않는 코드는 헤더에 변수를 정의하여 둘 이상의 파일 (포함되지 않음)에 포함되어 이름 충돌이 발생한다고 가정합니다. – falstro

답변

54

정적 데이터 멤버의 정의 자체가 템플릿입니다 때문입니다. 이것을 허용하는 것은 프로그램에서 여러 번 인라인되지 않는 함수 템플릿을 가질 수있는 것과 같은 이유로 필요합니다. 결과 엔티티 (예 : 함수 또는 정적 데이터 멤버)를 생성하려면 템플릿이 필요합니다. 표준 클래스 템플릿 외부 정의 템플릿 정의 말한다 - 정적 데이터 멤버의 정의를 넣어 허용되지 않을 경우, 인스턴스화 할 방법 무엇 T 알려진 아니에요

template<typename T> 
struct F { 
    static int const value; 
}; 

template<typename T> 
int const F<T>::value = sizeof(T); 

다음 매개 변수는 클래스 템플릿 소유자로부터 상속됩니다.


저는 GCC를 사용하여 몇 가지 실험을했습니다. 다음은 암시 적 구체화 인 F<float>::value과 명시 적 전문화 F<char>::value입니다. 여러 번 포함될 때 중복 된 기호 오류가 발생하지 않도록 .cpp 파일에 정의되어야합니다.

// Translation Unit 1 
template<typename T> 
struct F { 
    static int value; 
}; 

template<typename T> 
int F<T>::value = sizeof(T); 

// this would belong into a .cpp file 
template<> int F<char>::value = 2; 

// this implicitly instantiates F<float>::value 
int test = F<float>::value; 

int main() { } 

두 번째 번역 단위가 같은 정적 데이터 멤버 여기

template<typename T> 
struct F { 
    static int value; 
}; 

template<typename T> 
int F<T>::value = sizeof(T); 

int test1 = F<float>::value; 

의 또 다른 암시 적 인스턴스화를 우리가 GCC와 함께 얻을 수 있습니다 포함 -이 약한 심볼로 각각의 암시 적 인스턴스를 만들고으로 스틱 자체 섹션. 약한 기호는 링크 시간에 여러 기호가 존재할 때 오류를 일으키지 않습니다. 대신, 링커는 하나 개의 인스턴스를 선택하고 그들 모두를 가정 다른 사람이 우리가 F<float>::value는 링커가 링크 시간에 이러한 다중를 볼 수 있다는 것을 의미 약한 상징 볼 수 있습니다 그래서 같은

objdump -Ct main1.o # => 
# cut down to the important ones 
00000000 l df *ABS* 00000000 main1.cpp 
0000000a l  F .text 0000001e __static_initialization_and_destruction_0(int, int) 
00000000 l d .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE 
00000028 l  F .text 0000001c global constructors keyed to _ZN1FIcE5valueE 
00000000 g  O .data 00000004 F<char>::value 
00000000 g  O .bss 00000004 test 
00000000 g  F .text 0000000a main 
00000000 w O .data._ZN1FIfE5valueE 00000004 F<float>::value 

입니다 삭제합니다 .test, mainF<char>::value은 글로벌 (비 weak) 기호입니다. 함께 main1.omain2.o을 연결, 우리는지도를 출력 (-Wl,-M)이이 실제로는 모두 하나의 예를 제외하고는 떨어질 것을 나타냅니다

# (mangled name) 
.data._ZN1FIfE5valueE 
    0x080497ac  0x4 main1.o            
    0x080497ac    F<float>::value 

다음을 참조하십시오. 솔루션이 있습니다

+0

그러나 두 개의 "템플릿 템플릿 Templ :: templStatic = 0;"을 보는 링커는 어떻게됩니까? 정의 (unit1.cpp 및 unit2.cpp)가이 상황을 처리합니까? 객체 파일은 링커에게 말할 수있는 C++ 관련 메타 정보를 하나의 정의가 무시할 수 있습니다 (결과적으로 "다중 정의"링커 오류가 아닙니다)? – cybevnm

+0

님이 GCC 항목을 추가했습니다. –

1

, 당신은 부모 클래스를 만들고 그 안에 정적 변수를 넣을 수 있습니다, 당신의 템플릿 클래스는 개인적으로 그것을 상속하게 여기 예입니다 :

class Parent 
{ 
protected: 
    static long count; 
}; 

long Parent::count = 0; 

template<typename T> 
class TemplateClass: private Parent 
{ 
private: 
    int mKey; 
public: 
    TemplateClass():mKey(count++){} 
    long getKey(){return mKey;} 
} 

int main() 
{ 
    TemplateClass<int> obj1; 
    TemplateClass<double> obj2; 

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl; 
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl; 

    return 0; 
} 

출력은 다음과 같습니다

Object 1 key is: 0 
Object 2 key is: 1