.CPP 파일에 C ++ 템플릿 함수 정의 저장
헤더에 인라인 대신 CPP 파일에 저장하려는 템플릿 코드가 있습니다. 어떤 템플릿 유형이 사용 될지 알기 만하면 이것이 가능하다는 것을 알고 있습니다. 예를 들면 다음과 같습니다.
.h 파일
class foo
{
public:
template <typename T>
void do(const T& t);
};
.cpp 파일
template <typename T>
void foo::do(const T& t)
{
// Do something with t
}
template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);
마지막 두 줄-foo :: do 템플릿 함수는 ints 및 std :: strings에만 사용되므로 이러한 정의는 앱이 링크됨을 의미합니다.
내 질문은-이것은 불쾌한 해킹입니까 아니면 다른 컴파일러 / 링커와 함께 작동합니까? 현재이 코드를 VS2008에서만 사용하고 있지만 다른 환경으로 이식하려고합니다.
설명하는 문제는 헤더에서 템플릿을 정의하거나 위에서 설명한 접근 방식을 통해 해결할 수 있습니다.
C ++ FAQ Lite 에서 다음 사항을 읽는 것이 좋습니다 .
- 템플릿 클래스의 정의를 선언과 분리하여 .cpp 파일에 넣을 수없는 이유는 무엇입니까?
- 템플릿 기능에서 링커 오류를 피하려면 어떻게해야합니까?
- C ++ 키워드 내보내기는 템플릿 링커 오류에 어떻게 도움이됩니까?
이들은 이러한 (및 기타) 템플릿 문제에 대해 자세히 설명합니다.
이 페이지의 다른 사람들은 명시 적 템플릿 전문화 (또는 적어도 VS2008에서)에 대한 올바른 구문이 무엇인지 궁금합니다.
.h 파일에서 ...
template<typename T>
class foo
{
public:
void bar(const T &t);
};
그리고 .cpp 파일에서
template <class T>
void foo<T>::bar(const T &t)
{ }
// Explicit template instantiation
template class foo<int>;
이 코드는 올바른 형식입니다. 템플릿 정의는 인스턴스화 시점에서 볼 수 있다는 점에만주의해야합니다. 표준을 인용하기 위해 14.7.2.4 :
익스포트되지 않은 함수 템플리트, 익스포트되지 않은 멤버 함수 템플리트 또는 익스포트되지 않은 멤버 함수 또는 클래스 템플리트의 정적 데이터 멤버의 정의는 명시 적으로 인스턴스화 된 모든 변환 단위에 있어야합니다.
템플릿이 지원되는 모든 곳에서 잘 작동합니다. 명시 적 템플릿 인스턴스화는 C ++ 표준의 일부입니다.
귀하의 예는 정확하지만 이식성이 좋지 않습니다. @ namespace-sid가 지적한대로 사용할 수있는 약간 더 깔끔한 구문도 있습니다.
템플릿 클래스가 공유 할 라이브러리의 일부라고 가정합니다. 템플릿 화 된 클래스의 다른 버전을 컴파일해야합니까? 라이브러리 관리자는 클래스의 가능한 모든 템플릿 사용을 예상해야합니까?
대체 방법은 사용자가 가지고있는 약간의 변형입니다. 템플릿 구현 / 인스턴스 파일 인 세 번째 파일을 추가하십시오.
foo.h 파일
// Standard header file guards omitted
template <typename T>
class foo
{
public:
void bar(const T& t);
};
foo.cpp 파일
// Always include your headers
#include "foo.h"
template <typename T>
void foo::bar(const T& t)
{
// Do something with t
}
foo-impl.cpp 파일
// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;
한 가지주의해야 할 점은 컴파일하는 컴파일러 말할 필요가있다 foo-impl.cpp
대신 foo.cpp
후자는 아무것도하지 않는 컴파일 등을.
물론 세 번째 파일에 여러 구현을 사용하거나 사용하려는 각 유형에 대해 여러 구현 파일을 가질 수 있습니다.
이를 통해 다른 용도로 템플릿 클래스를 공유 할 때 훨씬 더 유연합니다.
이 설정은 또한 각 번역 단위에서 동일한 헤더 파일을 다시 컴파일하지 않기 때문에 재사용 된 클래스의 컴파일 시간을 줄입니다.
이것은 분명히 해킹은 아니지만 주어진 템플릿에 사용하려는 모든 클래스 / 유형에 대해 명시 적으로 템플릿을 지정해야한다는 사실을 알고 있어야합니다. 템플릿 인스턴스화를 요청하는 많은 유형의 경우 .cpp 파일에 많은 줄이있을 수 있습니다. 이 문제를 해결하려면 사용하는 모든 프로젝트에서 TemplateClassInst.cpp를 사용하여 인스턴스화 할 유형을보다 잘 제어 할 수 있습니다. 분명히이 솔루션은 ODR을 깨뜨릴 수 있으므로 완벽하지는 않습니다 (일명 은탄).
최신 표준에는 export
이 문제를 완화하는 데 도움이 되는 키워드 ( )가 있지만 Comeau 이외의 다른 컴파일러에서는 구현되지 않습니다.
이에 대한 FAQ를 참조하십시오 .
예, 이것이 전문화 명시 적 인스턴스화 를 수행하는 표준 방법 입니다. 언급했듯이이 템플릿을 다른 유형으로 인스턴스화 할 수 없습니다.
편집 : 의견에 따라 수정되었습니다.
이것이 템플릿 함수를 정의하는 표준 방법입니다. 템플릿을 정의하기 위해 읽은 세 가지 방법이 있다고 생각합니다. 또는 아마도 4. 장단점이있는 각각.
클래스 정의에서 정의하십시오. 클래스 정의는 엄격하게 참조하기 쉽고 읽기 쉬워야한다고 생각하기 때문에 전혀 마음에 들지 않습니다. 그러나 외부보다 클래스에서 템플릿을 정의하는 것이 훨씬 까다 롭지 않습니다. 모든 템플릿 선언이 동일한 수준의 복잡성에있는 것은 아닙니다. 이 방법은 또한 템플릿을 진정한 템플릿으로 만듭니다.
동일한 헤더에서 클래스 외부에 템플리트를 정의하십시오. 이것은 대부분 내가 선호하는 방법입니다. 클래스 정의를 깔끔하게 유지하고 템플릿은 실제 템플릿으로 유지됩니다. 그러나 까다로운 전체 템플릿 이름 지정이 필요합니다. 또한 코드는 모든 사람이 사용할 수 있습니다. 그러나 코드를 인라인으로 사용해야하는 경우 이것이 유일한 방법입니다. 클래스 정의 끝에 .INL 파일을 작성하여이를 수행 할 수도 있습니다.
main.CPP에 header.h 및 implementation.CPP를 포함하십시오. 그렇게 된 것 같아요. 사전 인스턴스화를 준비하지 않아도 실제 템플릿처럼 작동합니다. 내가 가진 문제는 자연스럽지 않다는 것입니다. 우리는 일반적으로 소스 파일을 포함하지 않으며 포함 할 것으로 예상합니다. 소스 파일을 포함했기 때문에 템플릿 기능을 인라인 할 수 있습니다.
게시 된 방식 인이 마지막 방법은 번호 3과 마찬가지로 소스 파일에 템플릿을 정의하는 것입니다. 그러나 소스 파일을 포함하는 대신 템플릿을 필요한 템플릿으로 미리 인스턴스화합니다. 이 방법에는 문제가 없으며 때로는 편리합니다. 우리는 하나의 큰 코드를 가지고 있는데, 인라인으로 얻을 수 없으므로 CPP 파일에 넣으십시오. 그리고 일반적인 인스턴스화를 알고 있으면 미리 정의 할 수 있습니다. 이렇게하면 기본적으로 같은 내용을 5, 10 번 쓰지 않아도됩니다. 이 방법은 코드를 독점적으로 유지하는 이점이 있습니다. 그러나 정기적으로 사용되는 작은 기능을 CPP 파일에 넣지 않는 것이 좋습니다. 이렇게하면 라이브러리의 성능이 저하됩니다.
bloated obj 파일의 결과를 알지 못합니다.
한 가지 예를 들어 보겠습니다. 어떤 이유로 템플릿 클래스를 원한다고 가정 해 봅시다.
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
이 코드를 Visual Studio로 컴파일하면 즉시 작동합니다. gcc는 링커 오류를 생성합니다 (여러 헤더 파일이 여러 .cpp 파일에서 사용되는 경우).
error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here
구현을 .cpp 파일로 옮길 수는 있지만 다음과 같이 클래스를 선언해야합니다.
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test();
template <>
void DemoT<bool>::test();
// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;
그리고 .cpp는 다음과 같습니다 :
//test_template.cpp:
#include "test_template.h"
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
헤더 파일에 마지막 두 줄이 없으면 gcc는 정상적으로 작동하지만 Visual Studio에서 오류가 발생합니다.
error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function
.dll 내보내기를 통해 함수를 노출하려는 경우 템플릿 클래스 구문은 선택 사항이지만 Windows 플랫폼에만 적용 가능하므로 test_template.h는 다음과 같습니다.
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif
template <>
void DLL_EXPORT DemoT<int>::test();
template <>
void DLL_EXPORT DemoT<bool>::test();
이전 예제의 .cpp 파일로.
그러나 링커에 더 많은 두통이 발생하므로 .dll 함수를 내 보내지 않으면 이전 예제를 사용하는 것이 좋습니다.
당신이 제시 한 예에는 아무런 문제가 없습니다. 그러나 함수 정의를 cpp 파일에 저장하는 것이 비효율적이라고 생각해야합니다. 나는 함수의 선언과 정의를 분리해야한다는 것을 이해합니다.
BCCL (부스트 개념 검사 라이브러리)을 명시 적 클래스 인스턴스화와 함께 사용하면 cpp 파일로 템플릿 함수 코드를 생성 할 수 있습니다.
업데이트 할 시간입니다! 인라인 (.inl 또는 다른 파일) 파일을 만들고 모든 정의를 복사하십시오. 각 기능 위에 템플릿을 추가해야합니다 ( template <typename T, ...>
). 이제 인라인 파일에 헤더 파일을 포함시키는 대신 반대로 수행하십시오. 클래스 선언 후 인라인 파일 을 포함하십시오 ( #include "file.inl"
).
왜 아무도 이것을 언급하지 않았는지 모르겠습니다. 즉각적인 단점은 없습니다.
참고 URL : https://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file
'Programming' 카테고리의 다른 글
Selenium WebDriver로 스크린 샷을 찍는 방법 (0) | 2020.02.14 |
---|---|
각 해시 맵에 대한 방법? (0) | 2020.02.14 |
React.js 인라인 스타일 모범 사례 (0) | 2020.02.14 |
ASP.NET MVC 뷰를 문자열로 렌더링하는 방법은 무엇입니까? (0) | 2020.02.14 |
파이썬 파일의 일반적인 헤더 형식은 무엇입니까? (0) | 2020.02.14 |