템플릿 클래스의 단일 메서드에 대한 템플릿 전문화


92

템플릿 클래스를 포함하는 다음 헤더가 적어도 두 개의 .CPP파일에 포함되어 있다는 점을 항상 고려하면 이 코드는 올바르게 컴파일됩니다.

template <class T>
class TClass 
{
public:
  void doSomething(std::vector<T> * v);
};

template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
  // Do something with a vector of a generic T
}

template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
  // Do something with a vector of int's
}

그러나 전문화 방법의 인라인에 유의하십시오. 메서드가 두 번 이상 정의되므로 링커 오류 (VS2008에서는 LNK2005)를 방지해야합니다. AFAIK 전체 템플릿 전문화는 간단한 메서드 정의와 동일하기 때문에 이것을 이해합니다.

그래서 어떻게 제거 inline합니까? 코드를 사용할 때마다 중복되어서는 안됩니다. 나는 Google을 검색하고 여기에서 몇 가지 질문을 읽고 많은 제안 된 솔루션을 시도했지만 성공적으로 구축되지 않았습니다 (적어도 VS 2008에서는 그렇지 않음).

감사!


4
인라인을 제거하려는 이유는 무엇입니까? 미학적으로 불쾌하다고 생각하십니까? 코드의 의미가 바뀐다 고 생각하십니까?
Martin York

1
이 방법이 "길다"고 여러 곳에서 사용된다면 바이너리 코드를 어디서나 복사 할 수 있기 때문입니다. 맞죠? 나는 이것을 질문에서 설명하려고했지만 명확하지 않은 것 같다 ... :)
Chuim

@Martin : 구현에 cpp 파일 대신이 헤더에 포함되어야하는 다른 코드가 많이 필요한 경우 어떻게해야합니까?
sbi

답변:


72

간단한 함수와 마찬가지로 선언 및 구현을 사용할 수 있습니다. 헤더 선언을 넣으십시오.

template <>
void TClass<int>::doSomething(std::vector<int> * v);

cpp 파일 중 하나에 구현을 넣으십시오.

template <>
void TClass<int>::doSomething(std::vector<int> * v) {
 // Do somtehing with a vector of int's
}

인라인을 제거하는 것을 잊지 마십시오 (이 솔루션이 작동하지 않을 것이라고 생각했습니다 :)). VC ++ 2005에서 확인


나는 적어도 이와 비슷한 것을 전에 시도했지만 다른 오류가 있었지만 이제는 inline복사 / 붙여 넣기 중에 제거하는 것을 잊었 음에 틀림 없다고 언급했습니다 . 이런 식으로 작동했습니다!
Chuim

템플릿이없는 함수에도 동일하게 적용됩니다 (클래스 메서드와 반대). 내 기능 전문화에 대해 동일한 링커 오류가 발생했습니다. 함수 전문화의 본문을 .cpp 파일로 옮기고 헤더에 전문화 선언을 남겼고 모든 것이 작동했습니다. 감사!
aldo apr

방금이 문제가 발생했고 위의 방법으로 해결되었습니다. 또한 컴파일러 가 템플릿 코드를 확장 하는 위치를 관리해야 합니다. 두 번 수행하면 컴파일러가 여러 정의에 대해 불평합니다.
Diederik 2013

4

전문화 정의를 CPP 파일로 이동해야합니다. 함수를 템플릿으로 선언하지 않아도 템플릿 클래스의 멤버 함수 특화가 가능합니다.


3

키워드를 인라인으로 제거 할 이유가 없습니다.
어쨌든 코드의 의미는 변경되지 않습니다.


질문 주석에서 복사 :이 방법이 "길고"여러 곳에서 사용된다면 바이너리 코드를 어디서나 복사 할 수 있기 때문입니다. 맞습니까? 질문에서 설명하려고했지만 명확하지 않은 것 같습니다 ... :)
Chuim

1
아니요. 링커는 추가 복사본을 제거합니다. 따라서 응용 프로그램 또는 lib 내에는 메서드의 인스턴스가 한 번만있을 것입니다.
Martin York

3
는 IF inline함수의 키워드 결과가 실제로 (표준 컴파일러 힌트로 정도 소요된다고) 인라인되고, 그 다음 그 여분의 복사본을 제거 할 수 없습니다. 그러나 이는 인라인에 대한 힌트 일뿐입니다 (주요 효과는 "특정 방식으로 링크 충돌에 오류를 생성하지 마십시오"라고 말하는 것입니다)
Yakk-Adam Nevraumont

2

어떤 이유로 든 인라인을 제거하려면 maxim1000의 솔루션이 완벽하게 유효합니다.

그러나 귀하의 의견에서 inline 키워드는 모든 내용이 포함 된 함수가 항상 인라인되지만 실제로 컴파일러 최적화에 크게 의존하는 AFAIK를 의미한다고 생각하는 것 같습니다.

C ++ FAQ 에서 인용

함수가 인라인임을 지정하는 방법에는 여러 가지가 있습니다. 그 중 일부는 인라인 키워드를 포함하고 다른 일부는 포함하지 않습니다. 함수를 인라인으로 지정하는 방법에 관계없이 컴파일러가 무시하도록 허용하는 요청입니다. 컴파일러는 인라인으로 지정된 함수를 호출하는 위치의 일부 또는 전부를 인라인 확장하거나 전혀 확장하지 않을 수 있습니다. (절망적으로 모호해 보이더라도 낙심하지 마십시오. 위의 유연성은 실제로 큰 이점입니다. 컴파일러가 큰 함수를 작은 함수와 다르게 처리 할 수있게 해주 며, 선택하면 디버그하기 쉬운 코드를 컴파일러가 생성 할 수 있습니다. 올바른 컴파일러 옵션.)

따라서 해당 함수가 실제로 실행 파일을 부 풀릴 것이라는 사실을 알지 못하거나 다른 이유로 템플릿 정의 헤더에서 제거하지 않는 한 실제로 해를 끼치 지 않고 그대로 둘 수 있습니다.


1

inline헤더 파일의 전문화도 그대로 두 려면 키워드를 그대로 두어야 할 좋은 이유가 있다고 덧붙이고 싶습니다 .

"직관적으로 완전히 전문화하면 더 이상 템플릿 매개 변수에 의존하지 않습니다. 따라서 전문화를 인라인으로 만들지 않는 한 .h 대신 .cpp 파일에 넣어야합니다. 그렇지 않으면 위반하게됩니다. 하나의 정의 규칙 ... "

참조 : https://stackoverflow.com/a/4445772/1294184


0

이것은 약간의 OT이지만 다른 사람을 도울 경우를 대비하여 여기에 남겨 둘 것이라고 생각했습니다. 나는 나를 여기로 이끈 템플릿 전문화에 대해 인터넷 검색을하고 있었고 @ maxim1000의 대답은 정확하고 궁극적으로 내 문제를 파악하는 데 도움이되었지만 충분히 명확하다고 생각하지 않았습니다.

내 상황은 OP와 약간 다릅니다 (그러나이 대답을 떠날만큼 충분히 유사합니다). 기본적으로 "상태 유형"을 정의하는 모든 종류의 클래스와 함께 타사 라이브러리를 사용하고 있습니다. 이러한 유형의 핵심은 단순히 enums이지만 클래스는 모두 공통 (추상) 부모에서 상속되며 연산자 오버로딩 및 함수와 같은 다른 유틸리티 함수를 제공 static toString(enum type)합니다. 각 상태 enum는 서로 다르며 관련이 없습니다. 예를 들어, 하나 enum에는 필드가 NORMAL, DEGRADED, INOPERABLE있고 다른 하나 에는 AVAILBLE, PENDING, MISSING등이 있습니다. 내 소프트웨어는 서로 다른 구성 요소에 대해 서로 다른 유형의 상태를 관리합니다. toString이러한 기능 을 활용하고 싶었다는 생각이 들었습니다.enum클래스이지만 추상적이기 때문에 직접 인스턴스화 할 수 없습니다. 사용하고 싶은 각 수업을 확장 할 수 있었지만 궁극적으로 내가 신경 쓰는 구체적인 상태가 되는 template수업 을 만들기로 결정했습니다 . 아마도 그 결정에 대해 약간의 논쟁이있을 수 있지만, 각 추상 클래스를 내 자신의 사용자 정의 클래스로 확장 하고 추상 함수를 구현하는 것보다 훨씬 적은 작업이라고 느꼈습니다 . 물론 내 코드에서 .NET의 문자열 표현 을 호출 하고 인쇄 할 수 있기를 원 했습니다 . 모든 s는 전혀 관련이 없었기 때문에 각각 고유 한typenameenumenum.toString(enum type)enumenumtoString(내가 배운 몇 가지 연구 후) 템플릿 전문화를 사용하여 호출해야했던 함수. 그것은 나를 여기로 이끌었습니다. 다음은이 작업을 올바르게 수행하기 위해 수행해야하는 MCVE입니다. 그리고 실제로 내 솔루션은 @ maxim1000과 약간 다릅니다.

이것은 s에 대한 (매우 단순화 된) 헤더 파일입니다 enum. 실제로 각 enum클래스는 자체 파일에 정의되어 있습니다. 이 파일은 내가 사용중인 라이브러리의 일부로 제공되는 헤더 파일을 나타냅니다.

// file enums.h
#include <string>

class Enum1
{
public:
  enum EnumerationItem
  {
    BEARS1,
    BEARS2,
    BEARS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

class Enum2
{
public:
  enum EnumerationItem
  {
    TIGERS1,
    TIGERS2,
    TIGERS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

다음 파일을 다른 코드 블록으로 분리하기 위해이 줄을 추가합니다.

// file TemplateExample.h
#include <string>

template <typename T>
class TemplateExample
{
public:
  TemplateExample(T t);
  virtual ~TemplateExample();

  // this is the function I was most concerned about. Unlike @maxim1000's
  // answer where (s)he declared it outside the class with full template
  // parameters, I was able to keep mine declared in the class just like
  // this
  std::string toString();

private:
  T type_;
};

template <typename T>
TemplateExample<T>::TemplateExample(T t)
  : type_(t)
{

}

template <typename T>
TemplateExample<T>::~TemplateExample()
{

}

다음 파일

// file TemplateExample.cpp
#include <string>

#include "enums.h"
#include "TemplateExample.h"

// for each enum type, I specify a different toString method, and the
// correct one gets called when I call it on that type.
template <>
std::string TemplateExample<Enum1::EnumerationItem>::toString()
{
  return Enum1::toString(type_);
}

template <>
std::string TemplateExample<Enum2::EnumerationItem>::toString()
{
  return Enum2::toString(type_);
}

다음 파일

// and finally, main.cpp
#include <iostream>
#include "TemplateExample.h"
#include "enums.h"

int main()
{
  TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1);
  TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3);

  std::cout << t1.toString() << std::endl;
  std::cout << t2.toString() << std::endl;

  return 0;
}

그리고 이것은 다음을 출력합니다.

BEARS1
TIGERS3

이것이 내 문제를 해결하는 이상적인 솔루션인지는 알 수 없지만 저에게 효과적이었습니다. 이제 얼마나 많은 열거 형을 사용하든 상관없이 toString.cpp 파일 의 메서드에 대해 몇 줄을 추가하기 만하면됩니다. 라이브러리에서 이미 정의 된 toString메서드를 직접 구현하지 않고 각각을 확장하지 않고도 사용할 수 있습니다. enum사용하고 싶은 수업.

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