![](/img/trans.png)
[英]c++ class template can be instantiated but a function template instantiation with the same template parameters fails
[英]C++ template instantiation of function template parameters
我使用模板實例化[*]有以下問題。
文件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_);
}
file caller.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文件中知道它們,除非你明確地將那個cc文件#include
到頭文件或調用它們的文件中,這是由於解析器工作。
(這就是為什么模板函數定義通常保存在頭文件中,正如Earwicker所描述的那樣。)
有更清楚的嗎?
我相信Earwicker是正確的。 在這種情況下顯式實例化模板成員函數func的問題是boost :: bind返回的類型是依賴於實現的。 它不是 boost :: function。 boost :: function可以包含 boost:bind,因為它有一個模板賦值運算符,可以推導出右側的類型(boost :: bind結果)。 在caller.cc中func的這種特殊用法中,使用boost的這個特定實現,boost :: bind的類型實際上是<和>之間的鏈接器錯誤中提到的類型(即boost::_bi::bind_t...
) 但是明確地為該類型實例化func可能會有可移植性問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.