如果你想MyClass
是可以容納兩個free函數的類型 指針模板:
double (*)(const T &var);
bool (*)(const T &var);
對於一些參數類型T
,或者成員函數的類型 指針:
double (C::*)(const T &var);
bool (C::*)(const T &var);
對於某些參數類型C
和T
然後,MyClass
必須同時T
和C
進行參數 ,你需要兩個專業:
- 哪裏
C
是一些非類類型
- 凡
C
是任何類類型
萬一(1),非類類型C
不可能具有成員函數 ,以便人們可以實現自由函數指針專用化。
在情況(2)中,類C
可以是具有成員函數的類,因此一個 將實現成員函數指針專用化。
非班級類型C
的明顯選擇是void
。因此,我們可以讓C
默認爲void
:
主模板
template<typename T, typename C = void>
struct MyClass;
這樣:
MyClass<T>
將是T
free函數指針專業化和:
MyClass<T,C>
對於除void
以外的任何C
,將是成員函數指針專用化。
正如你可能知道你可以用std::enable_if
和SFINAE
使編譯器 選擇一個模板類的一個專業方向或另一個,這取決於它的模板之一 參數是否U
滿足一些編譯時的測試。你可以採取 即接近這裏,但另外一個是可用,不需要設備:
與主模板開始,我們只是想有:
自由功能專業化
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
和:
會員功能專業化
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
但是我們不能這樣做,因爲成員函數「specialization」具有與主模板完全相同的模板參數 。這意味着它不是 專業化,編譯器將不會允許它。
但是,只需簡單地爲主要 模板提供一個不需要的默認模板參數,但其存在的 即可讓這兩個專業化站點都可以輕鬆解決該問題。
新的主模板
template <typename T, typename C = void, typename Default = void>
struct MyClass;
因此,這裏是一個說明性的解決方案:
// Primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
// Free function specialization
template <typename T>
struct MyClass<T>
{
using firstFunctor_t = double(*)(T const &);
using secondFunctor_t = bool(*)(T const &);
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(T const & var) {
return _firstFunc(var);
}
bool callSecond(T const & var) {
return _secondFunc(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
using firstFunctor_t = double(C::*)(T const &);
using secondFunctor_t = bool(C::*)(T const &) const;
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(C & obj, T const & var) {
return (obj.*_firstFunc)(var);
}
double callFirst(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_firstFunc)(var);
}
bool callSecond(C & obj, T const & var) {
return (obj.*_secondFunc)(var);
}
bool callSecond(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_secondFunc)(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
在成員函數專業化,發現幾個百分點,你可能 沒有考慮: -
我決定我要存儲的第二個成員函數應該是 const成員函數。 C
參數T const &
的參數並返回bool將會是const
成員 函數的成員函數更多,不是嗎?如果是的話,那麼const-ness
必須的 我在專業化使用成員函數類型定義部分:
using secondFunctor_t = bool(C::*)(T const &) const;
或嘗試實例化專業化與任何bool (C::*)(T const &) const
將無法編譯。
另外,我提供了兩個重載每個MyClass<T,C>::callFirst
和MyClass<T,C>::callSecond
,一個帶有參數:
C & obj, T const & var
,另一個參數:
C const & obj, T const & var
沒有第二,嘗試或者調用MyClass<T,C>::callFirst
或MyClass<T,C>::callSecond
與obj
這是常量將無法 編譯。
對於程序演示該解決方案可以追加:
#include <iostream>
#include <string>
double foo(std::string const & s)
{
return std::stod(s);
}
bool bar(std::string const & s)
{
return s.size() > 0;
}
struct SomeClass
{
SomeClass(){};
double foo(std::string const & s) {
return ::foo(s);
}
bool bar(std::string const & s) const {
return ::bar(s);
}
};
int main()
{
MyClass<std::string> my0{foo,bar};
std::cout << std::boolalpha;
std::cout << my0.callFirst("1.11") << std::endl;
std::cout << my0.callSecond("Hello World") << std::endl;
MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
SomeClass thing;
std::cout << my1.callFirst(thing,"2.22") << std::endl;
std::cout << my1.callSecond(thing,"Hello World") << std::endl;
SomeClass const constThing;
std::cout << my1.callFirst(constThing,"3.33") << std::endl;
std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
return 0;
}
See it live
你說,你想這個模板是「非常靈活」。 圖解解決方案適合您的示例,但您可能對 感興趣,知道它不是差不多儘可能靈活。 對於免費函數和成員函數,還有其他variadic template 參數,您的模板可以使用任意類型的任意參數的任意返回類型和任意數量來存儲和調用[成員]函數。 請參閱this question和 的答案。
「函數指針指向任意類」的問題是你需要一個「任意類的對象」來使用它。不,'void *'將不起作用。你最終可能會用'std :: bind'和'std :: function',因爲它不再依賴於無限的任意類。 – MSalters