0

헤더에 제공하려는 정의로 인해 중복 심볼 오류가 발생했습니다. Minimal, Complete, and Verifiable example의 오류는 다음과 같습니다. 헤더 파일과 소스 파일은 아래와 같습니다.헤더의 전문화로 인해 중복 된 기호를 피할 수 있습니까?

$ clang++ main.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt 
duplicate symbol Id<S>::id in: 
    /tmp/main-3f2415.o 
    /tmp/long long-d62c28.o 
duplicate symbol Id<T>::id in: 
    /tmp/main-3f2415.o 
    /tmp/long long-d62c28.o 
duplicate symbol Id<S>::id in: 
    /tmp/main-3f2415.o 
    /tmp/unsigned long long-bfa6de.o 
duplicate symbol Id<T>::id in: 
    /tmp/main-3f2415.o 
    /tmp/unsigned long long-bfa6de.o 
ld: 4 duplicate symbols for architecture x86_64 

여기에 비슷한 질문이 있지만 특수화가 필요하지 않습니다 : Static member initialization in a class template. 이 질문은 전문화되어 있지만 MSVC가 아니라 Clang : How to initialize a static member of a parametrized-template class입니다. 그리고이 질문 상태는 소스 (* .CPP) 파일을 넣어하지만 우리는 방지하기 위해 헤더 파일을 연타 3.8 및 'Id<S>::id' required here, but no definition is available 경고를 목표로하고 있습니다 : Where should the definition of an explicit specialization of a class template be placed in C++?

GCC, ICC, MSVC, SunCC과 XLC은 OK입니다 초기화. Clang과 LLVM이 나에게 문제를주고있다. Clang과 LLVM은 또한 전문화 된 구체화와 extern의 인스턴스화에 문제가있어 특별한 종류의 지옥을 소유하고 있습니다.

우리는 C++ 03을 지원하지만 C++ 17이므로이 솔루션을 조심해야합니다. 순진하게도, 번역 유니트를 이스케이프 처리하지 못하도록 이름없는 네임 스페이스에 특수화 초기화를 시도했지만 컴파일 오류가 발생했습니다.

헤더의 템플릿 클래스를 초기화하고 특수화 할 때 중복 심볼 정의가 발생하지 않도록 어떻게해야합니까?


아래는 cat main.cpp a.h s.h s.cpp t.h t.cpp x.cpp y.cpp 인 MCVE입니다. 문제는 전문화 및 초기화를 제공하는 a.h 인 것으로 보입니다. 소스 파일 x.cppy.cpp (a.h 포함).

MAIN.CPP

#include "a.h" 
#include "s.h" 
#include "t.h" 

int main(int argc, char* argv[]) 
{ 
    uint8_t s = Id<S>::id; 
    uint8_t t = Id<T>::id; 
    return 0; 
} 

a.h

#ifndef A_INCLUDED 
#define A_INCLUDED 

#include <stdint.h> 

template <class T> class Id 
{ 
public: 
    static const uint8_t id; 
}; 

class S; 
class T; 

template<> const uint8_t Id<S>::id = 0x01; 
template<> const uint8_t Id<T>::id = 0x02; 

#endif 

s.h

,451,515,
#ifndef S_INCLUDED 
#define S_INCLUDED 

class S { 
public: 
    S(); 
}; 

#endif 

s.cpp

#include "s.h" 

S::S() {} 

t.h

#ifndef T_INCLUDED 
#define T_INCLUDED 

class T { 
public: 
    T(); 
}; 

#endif 

t.cpp

,
#include "t.h" 

T::T() {} 

x.CPP

#include "a.h" 

y.cpp

#include "a.h" 
+0

'인라인'도움이되지 않습니까? – user0042

+0

@ user0042 - 아니요, 인라인 컴파일에 실패했습니다. 수업과 전문 분야 모두에서 실패합니다. – jww

답변

1

unnamed namespace을 사용하면 클래스에 내부 연결이 가능해집니다.

#ifndef A_INCLUDED 
#define A_INCLUDED 

#include <stdint.h> 

namespace { 
    template <class T> class Id 
    { 
    public: 
     static const uint8_t id; 
    }; 
} 

class S; 
class T; 

template<> const uint8_t Id<S>::id = 0x01; 
template<> const uint8_t Id<T>::id = 0x02; 

#endif 

Id<T> 하나의 번역 단위가 다른 번역 단위에서의 그것과 다르기 때문에 다음 인해 내부 연결로 작동합니다, 귀하의 예제에서 a.h의 내용이 코드를 교체하여 하나의 정의 규칙 위반되지 않는다 .

6

연타/LLVM은 문제가되지 않습니다. 진단 기능이 필요없이 정의되지 않은 동작을 실행 중입니다. 수정은 간단합니다. 전문화를 번역 단위 중 하나에 넣어야합니다. 즉,

a.cpp :

#include "a.h" 

template<> const uint8_t Id<S>::id = 0x01; 
template<> const uint8_t Id<T>::id = 0x02; 

그런 다음 명령 행 :

clang++ main.cpp a.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt 

봐라. 그것은 작동합니다.

+0

감사합니다. 'a.cpp '가 없습니다. 헤더에 어떻게 보관합니까? 아니면 단순히 Clang과 LLVM에서 할 수 없습니까? (* .cpp 파일을 사용해 보았지만, Clang은 인스턴스 생성시 누락 된 정의에 대해 불평했다.) – jww

+0

* 여러 번 * 포함하는 머리글에 보관하지 마십시오. 'a.h '를 여러 번역 단위에 여러 번 포함하기 때문에 여러 정의 오류가 있습니다. 당신이해야 할 일은 앞으로해야 할 것을 선언하고 헌신 된'* .cpp'에서 할 수있는 것을 인스턴스화하는 것입니다.한 번에 전체 프로젝트를 컴파일하고 링크 할 수 있지만 보장되지는 않습니다 - '번역 단위'의 정의가 다소 퍼지기도합니다 (예 : 반드시 * * .cpp라는 하나의 *. 파일). – user268396

+1

@ user268396 죄송하지만 잘못된 것입니다. 번역 단위는 [\ [lex.separate \]] (http://eel.is/c++draft/lex#separate-1)에 명시 적으로 정의되어 있습니다. "프로그램의 텍스트는 다음과 같은 소스 파일 단위로 유지됩니다. 이 문서. 전처리 지시문 #include를 통해 포함 된 모든 헤더 및 소스 파일과 함께 조건부 포함 사전 처리 지시문에서 건너 뛴 모든 소스 행을 번역 단위라고합니다. " 단일'.cpp' 파일에서 인스턴스화하는 부분은 정확합니다. – user9139968

3

Id :: id 및 Id :: id에 대한 ODR (하나의 정의 규칙)을 위반합니다. 링크가 포함 된 각 번역 단위에 대해 정의되므로 연결시 표시됩니다.

클래스 S 및 T에 대한 ID의 의도에 따라 고유 한 집을 제공해야합니다. 하나는 main.cpp에 주차하는 것일 수 있습니다. MAIN.CPP

template<> const uint8_t Id<S>::id = 0x01; 
template<> const uint8_t Id<T>::id = 0x02; 

또는를 추가, t.cpp에 s.cpp 및 T의 ID에 S의 ID를 넣어 :

s.cpp

#include "s.h" 
#include "a.h" 

template<> const uint8_t Id<S>::id = 0x01; 

및 t.cpp에 해당하는 내용입니다.

a.h에서 S와 T의 흔적을 삭제하는 것을 잊지 마십시오.

이 a.h 인터페이스의 일부인 경우 a.cpp를 만들고 거기에 정의하십시오.