2017-02-14 11 views
1

정적 인 (constexpr) 멤버의 연결에 대한 많은 질문이 있습니다.정적 constexpr 멤버가있는 템플릿 클래스의 ODR

하지만 템플릿 클래스를 아웃 오브 라인 정의로 사용하는 것이 헤더 파일에서 작동하지만 특수 클래스에서는 작동하지 않는 이유가 궁금합니다.

이 링커 오류없이 작동하는)

:

template<typename, typename> 
struct Foobar; 

template<typename T> 
struct Foobar<int, T> { 
    static constexpr std::array<int, 1> a = {{1}}; 
}; 

template<typename T> 
constexpr std::array<int, 1> Foobar<int, T>::a; 

// foo.cpp 
std::cout << Foobar<int, int>::a[0] << "\n"; 

// bar.cpp 
std::cout << Foobar<int, int>::a[0] << "\n"; 

의 objdump를 :

foo.o : 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE

bar.o : 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE

링크 된 파일 : 0000000000475a30 w O .rodata 0000000000000004 _Z6FoobarIiiE1aE

b) 하지 않습니다 (다중 정의) :

template<typename> 
struct Foobar; 

template<> 
struct Foobar<int> { 
    static constexpr std::array<int, 1> a = {{1}}; 
}; 
constexpr std::array<int, 1> Foobar<int>::a; 

// foo.cpp 
std::cout << Foobar<int>::a[0] << "\n"; 

// bar.cpp 
std::cout << Foobar<int>::a[0] << "\n"; 

의 objdump를 :

foo.o 0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE

bar.o : 0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE 우리가 볼 무엇

, 아웃 오브 라인 정의 오브젝트 파일 안에 다른 주소를 가지고있다 (예제 b)). 당신에게

내 질문 :

  1. 이 템플릿 트릭을 사용하여 저장할 수 있나요? 단점은 무엇입니까?
  2. 미래의 b와 같은 경우 odr의 정의를 완화하는 것이 유용할까요?

미리 감사드립니다.

+1

"* 미래의 나처럼 이러한 경우에 대한 ODR의 정의를 휴식하는 것이 유용 할 것인가 *?"이미 C++ 17 : constexpr 정적 데이터 멤버는 암묵적으로'inline'입니다. – ildjarn

+0

멋진! 그것을 기다릴 수 없어. :) – Viatorus

답변

3

보기 [basic.def.odr]/6

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...

이 규칙의 효과가 인라인 모든 템플릿 선언 것처럼 행동한다 인 점이다. 이다 따라서 템플릿 선언 인 당신이

template<typename T> 
constexpr std::array<int, 1> Foobar<int, T>::a; 

이 첫 번째 조각에서

을 (그러나. 명시 적 인스턴스화명시 적 전문성 선언으로 확장되지 않음) 및 다중으로 정의 될 수있다. 두 번째 조각, 당신은 템플릿 선언하지

constexpr std::array<int, 1> Foobar<int>::a; 

을 가지고 정의 자체가 정의되고있는 것은 템플릿의 특수화 될 일이 있더라도, 템플릿되지 않습니다.

My question to you:

  1. Is it save to use the template trick? What are the disadvantage?

여기에는 "트릭"이 없습니다.에 대한 멤버를 모두 정의하려는 경우 모두Foo<T> 인 경우 헤더 파일에 정의를 추가 할 수밖에 없습니다. 의 멤버를Foo<T>과 같이 Foo<int>과 같이 정의하려면 헤더 파일에 정의를 넣지 않아야합니다 (인라인 변수가있는 C++ 17까지). 사용자가 의도 한대로 트릭이 없습니다. 할 일은 구체적인 목표에 달려 있습니다.

(두 번째 질문은 코멘트 섹션에 대답했다.)