C ++ 템플릿 기능 템플릿 매개 변수의 인스턴스화
문제
템플릿 인스턴스화를 사용하는 다음과 같은 문제가 있습니다 [*].
파일 foo.h
class Foo
{
public:
template <typename F>
void func(F f)
private:
int member_;
};
파일 foo.cc
template <typename F>
Foo::func(F f)
{
f(member_);
}
파일 발신자 .CC
Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1));
이것은 정상적으로 컴파일하지만 링커는 정의되지 않은 기호에 대해 불평합니다.
void Foo::func<boost::_bi::bind_t...>
어떻게 인스턴스화 할 수 있습니까? 기능 Foo::func
? 논쟁으로 기능을 취하기 때문에 약간 혼란 스럽습니다. 인스턴스화 함수를 추가하려고했습니다 foo.cc, 나는 정기적으로 익숙합니다 비 기능 유형 :
instantiate()
{
template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>);
}
분명히 이것은 작동하지 않습니다. 누군가가 나를 올바른 방향으로 가리킬 수 있다면 감사하겠습니다.
감사!
*] 예, Parashift FAQ Lite를 읽었습니다.
해결책
이것에 대한 답은 컴파일러 종속입니다. SUN C ++ 컴파일러의 일부 버전은 별도의 번역 장치에서 공유되는 템플릿 기능 구현 캐시를 구축하여이를 자동으로 처리합니다.
Visual C ++ 및이를 수행 할 수없는 다른 컴파일러를 사용하는 경우 기능 정의를 헤더에 넣을 수도 있습니다.
헤더가 여러 .cc 파일에 포함 된 경우 중복 정의에 대해 걱정하지 마십시오. 컴파일러는 특수 속성으로 템플릿 생성 메소드를 표시하므로 링커는 불만 대신 중복을 버리는 것을 알고 있습니다. 이것이 C ++가 "하나의 정의 규칙"을 가진 이유 중 하나입니다.
편집하다: 위의 주석은 템플릿이 주어진 유형 매개 변수를 연결할 수 있어야하는 일반적인 경우에 적용됩니다. 클라이언트가 사용할 닫힌 유형 세트를 알고 있다면 템플릿의 구현 파일에서 명시적인 인스턴스화를 사용하여 고객이 사용할 수있게되므로 컴파일러가 다른 파일에 대한 정의를 생성 할 수 있습니다. 그러나 템플릿이 클라이언트에게만 알려진 유형으로 작동 해야하는 일반적인 경우 템플릿을 헤더 파일로 분리하고 구현 파일로 분리하는 데는 점이 거의 없습니다. 모든 고객은 어쨌든 두 부분을 모두 포함해야합니다. 클라이언트를 복잡한 종속성에서 분리하려면 비 모전 함수 뒤에 해당 종속성을 숨기고 템플릿 코드에서 호출하십시오.
다른 팁
원하는 것처럼 파일로 분할 :
나는 이것을 추천하지 않습니다. 가능하다는 것을 보여줍니다.
plop.h
#include <iostream>
class Foo
{
public:
Foo(): member_(15){}
// Note No definition of this in a header file.
// It is defined in plop.cpp and a single instantiation forced
// Without actually using it.
template <typename F>
void func(F f);
private:
int member_;
};
struct Bar
{
void bar_func(int val) { std::cout << val << "\n"; }
};
struct Tar
{
void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";}
};
plop.cpp
#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>
template <typename F>
void Foo::func(F f)
{
f(member_);
}
// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc;
// Force the compiler to generate an instantiation of Foo::func()
template void Foo::func<myFunc>(myFunc f);
// Note this is not a specialization as that requires the <> after template.
// See main.cpp for an example of specialization.
main.cpp
#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>
// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar;
// Specialization of Foo::func()
template<> void Foo::func<myTar>(myTar f)
{
std::cout << "Special\n";
}
// Note. This is not instantiated unless it is used.
// But because it is used in main() we get a version.
int main(int argc,char* argv[])
{
Foo f;
Bar b;
Tar t;
f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp
f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization
}
foo.cc를 caller.cc에 포함시키고 있습니까? 인스턴스화는 컴파일 시간에 발생하는 일입니다. 컴파일러가 호출자의 호출을 볼 때 템플릿의 인스턴스화 버전을 만들지 만 전체 정의를 사용할 수 있어야합니다.
나는 그들이 모두 언급하는 것은 템플릿 함수 정의 (선언뿐만 아니라)가 사용되는 파일에 포함되어야한다는 것입니다. 템플릿 기능은 실제로 사용될 때까지 존재하지 않습니다. 별도의 CC 파일에 넣으면 컴파일러는 다른 CC 파일에서 알지 못합니다. #include
이 CC 파일은 파서의 작동 방식으로 인해 헤더 파일 또는 호출되는 파일로 파일에 있습니다.
(earwicker가 설명한대로 템플릿 함수 정의가 일반적으로 헤더 파일에 보관되는 이유입니다.)
더 명확합니까?
나는 Earwicker가 정확하다고 생각합니다. 이 경우 템플릿 멤버 함수 기능을 명시 적으로 인스턴스화하는 문제는 boost :: bind가 구현 종속적이라는 것입니다. 그것은이다 ~ 아니다 부스트 :: 기능. 부스트 :: 기능이 가능합니다 포함하다 부스트 : 오른쪽 유형을 추론하는 템플릿 할당 연산자가 있기 때문에 바인딩하십시오 (부스트 :: 바인드 결과). Caller.cc에서 이러한 특별한 사용 에서이 특정 부스트를 구현하면 Boost :: Bind의 유형은 실제로 <및> (즉) 사이의 링커 오류에 언급 된 유형입니다. boost::_bi::bind_t...
). 그러나 해당 유형의 기능을 명시 적으로 인스턴스화하면 휴대 성 문제가있을 수 있습니다.