다른 개체를 사용할 때 템플릿 전문화의 다중 정의


96

다른 개체 파일에서 특수 템플릿을 사용하면 연결할 때 "다중 정의"오류가 발생합니다. 내가 찾은 유일한 해결책은 "인라인"기능을 사용하는 것이지만 일부 해결 방법처럼 보입니다. "인라인"키워드를 사용하지 않고 어떻게 해결합니까? 가능하지 않다면 왜?

다음은 예제 코드입니다.

paulo@aeris:~/teste/cpp/redef$ cat hello.h 
#ifndef TEMPLATE_H
#define TEMPLATE_H

#include <iostream>

template <class T>
class Hello
{
public:
    void print_hello(T var);
};

template <class T>
void Hello<T>::print_hello(T var)
{
    std::cout << "Hello generic function " << var << "\n";
}

template <> //inline
void Hello<int>::print_hello(int var)
{
    std::cout << "Hello specialized function " << var << "\n";
}

#endif

paulo@aeris:~/teste/cpp/redef$ cat other.h 
#include <iostream>

void other_func();

paulo@aeris:~/teste/cpp/redef$ cat other.c 
#include "other.h"

#include "hello.h"

void other_func()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);
}

paulo@aeris:~/teste/cpp/redef$ cat main.c 
#include "hello.h"

#include "other.h"

int main()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);

    other_func();

    return 0;
}

paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
    g++ -c other.c -o other.o -Wall -Wextra
    g++ main.c other.o -o main -Wall -Wextra

드디어:

paulo@aeris:~/teste/cpp/redef$ make
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
other.o: In function `Hello<int>::print_hello(int)':
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)'
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: ** [all] Erro 1

hello.h 내부의 "인라인"의 주석 처리를 제거하면 코드가 컴파일되고 실행되지만 일종의 "해결 방법"처럼 보입니다. 특수 함수가 크고 여러 번 사용되면 어떻게 될까요? 큰 바이너리를 얻을 수 있습니까? 이 작업을 수행하는 다른 방법이 있습니까? 그렇다면 어떻게? 그렇지 않다면 왜?

답을 찾아 보려고했지만 추가 설명없이 "인라인으로 사용"하는 것이 전부였습니다.

감사


7
실제 특수 구현을 헤더 파일이 아닌 .cpp에 넣습니다
Anycorn dec

답변:


130

직관적으로 어떤 것을 완전히 전문화하면 더 이상 템플릿 매개 변수에 의존하지 않습니다. 따라서 전문화를 인라인으로 만들지 않는 한 .h 대신 .cpp 파일에 넣어야합니다. 그렇지 않으면 David가 말한 것처럼 하나의 정의 규칙. 템플릿을 부분적으로 전문화하는 경우 부분 전문화는 여전히 하나 이상의 템플릿 매개 변수에 의존하므로 .h 파일로 이동합니다.


흠 나는 그것이 ODR을 어떻게 깨뜨리는 지에 대해 여전히 약간 혼란 스럽습니다. 완전히 전문화 된 템플릿을 한 번만 정의하기 때문입니다. 다른 개체 파일에서 개체를 여러 번 만들 수 있지만 (이 경우 other.c 및 main.c에서 인스턴스화 됨) 원래 개체 자체는 하나의 파일에서만 정의됩니다 (이 경우) hello.h.
Justin Liang

3
@JustinLiang : 헤더는 두 개의 개별 .c 파일에 포함되어 있습니다. 이는 관련 위치에 포함 된 파일에 콘텐츠 (전체 전문화 포함)를 직접 작성한 것과 동일한 효과를가집니다. 하나의 정의 규칙 ( en.wikipedia.org/wiki/One_Definition_Rule 참조 )은 (다른 것들 중에서) 다음과 같이 말합니다. "전체 프로그램에서 객체 또는 비 인라인 함수는 둘 이상의 정의를 가질 수 없습니다." 이 경우 함수 템플릿의 전체 전문화는 본질적으로 일반 함수와 동일하므로 인라인이 아니면 둘 이상의 정의를 가질 수 없습니다.
Stuart Golodetz 2015

흠, 템플릿 화 된 전문화가 없으면이 오류가 나타나지 않습니다. 클래스 외부의 헤더 파일에 정의 된 두 개의 다른 함수가 있다고 가정 해 보겠습니다. 인라인 없이도 여전히 작동합니까? 예 : pastebin.com/raw.php?i=bRaiNC7M . 나는 그 수업을 듣고 두 개의 파일에 포함 시켰습니다. 두 파일에 직접 "내용을 기록한 것과 같은 효과"가 발생하지 않아 다중 정의 오류가 발생하지 않습니까?
Justin Liang

@Justin Liang, 클래스 기반 헤더 코드는 함수 정의가 클래스 본문 내부에 있지 않는 한 여러 파일에 포함 된 경우 여전히 ODR을 위반합니다.
haripkannan

따라서 내 정적 멤버 정의가 앞에 template <typename T>오면 헤더에 들어갈 template<>수 있고 그렇지 않으면 그렇지 않을 수 있습니까?
Violet Giraffe

49

이 키워드 inline는 컴파일러가 수행할지 여부를 결정할 수있는 실제 인라인에 대한 것보다 단일 정의 규칙을 위반하지 않고 둘 이상의 객체 파일에 심볼이 존재한다는 것을 컴파일러에 알리는 것입니다.

지금보고있는 문제는 인라인이 없으면 함수가 헤더를 포함하는 모든 변환 단위로 컴파일되어 ODR을 위반한다는 것입니다. 추가 inline하는 것이 올바른 방법입니다. 그렇지 않으면 다른 기능과 마찬가지로 전문화를 전달하여 단일 번역 단위로 제공 할 수 있습니다.


22

헤더 ( void Hello<T>::print_hello(T var)) 에서 템플릿을 명시 적으로 인스턴스화했습니다 . 이렇게하면 여러 정의가 생성됩니다. 두 가지 방법으로 해결할 수 있습니다.

1) 인스턴스화를 인라인으로 만드십시오.

2) 헤더에서 인스턴스화를 선언 한 다음 cpp에서 구현합니다.


실제로 C. 정적을 가진 유사한 이름없는 네임 스페이스 더 ... 그 넣을 수있는 제 3의 방법이
알렉시스 WILKE을

4
여기서는 유효하지 않습니다. 템플릿 전문화는 원본 템플릿과 동일한 네임 스페이스에 있어야합니다.
Edward Strange

0

여기에 몇 가지있다 조각 이 문제와 관련된 11 표준 C ++의는 :

함수 템플릿의 명시 적 특수화는 함수 템플릿이 인라인인지 여부와 관계없이 인라인 지정자로 선언되거나 삭제 된 것으로 정의 된 경우에만 인라인됩니다. [ 예:

템플릿 void f (T) {/ * ... /} 템플릿 인라인 T g (T) {/ ... * /}

template <> inline void f <> (int) {/ * ... /} // OK : inline template <> int g <> (int) {/ ... * /} // OK : not inline — end 예 ]

따라서 파일에서 템플릿의 명시 적 (일명 전체) 전문화 *.h를 수행하는 경우에도 ODRinline 위반을 제거하는 데 도움이 필요 합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.