を(1)の場合、非クラス型C
はメンバ関数 を持つことができないため、自由関数ポインタの特殊化を実装します。 ケース(2)の場合、クラスC
はメンバ関数を持つクラスであるため、 はメンバ関数ポインタの特殊化を実装します。
非クラス型のための明らかな選択C
はvoid
です。だから
プライマリテンプレート
template<typename T, typename C = void>
struct MyClass;
:
MyClass<T>
T
のための無料の関数ポインタ専門になり、そして:
だから我々はvoid
からC
をデフォルトにすることができますMyClass<T,C>
void
以外の任意のC
の場合は、メンバー関数ポインタの特殊化になります。
あなたは、コンパイラ は、そのテンプレートの1 がU
満たすいくつかのコンパイル時のテストパラメータかどうかに応じて、1つのクラステンプレートの専門または別のものを選んだ作るためにstd::enable_if
とSFINAE
を使用することができます知っているかもしれませんが。あなたがここに近づい を取ることができるが、別の1はその装置を必要としないが利用可能です:
主なテンプレートを皮切りに、私たちは持っているしたいと思います:
フリー機能の特殊
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
と:
メンバ関数の特殊
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
しかし、メンバ関数 "specialization"は、正確に プライマリテンプレートと同じテンプレートパラメータを持っているため、これを行うことはできません。つまり、は ではなく、コンパイラはそれを許可しません。
しかし、プライマリ テンプレートに不要なデフォルトのテンプレートパラメータを1つ追加するだけで、それらの特殊化が両方を許可する プレゼンスが存在するようにすることで、簡単にその問題を取り除くことができます。だからここ
新しいプライマリテンプレート
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;
};
、あなたが と考えられていない可能性がありますポイントのカップルに気づく:を -
私が保存したい第2のメンバー機能は constメンバ関数です。メンバー関数C
がT const &
という引数をとり、boolを返す可能性は、const
メンバー の関数になるでしょうか?そしてもしそうなら、const-ness
は私が専門で使用 メンバー関数の型定義の一部でなければならないこと:
using secondFunctor_t = bool(C::*)(T const &) const;
または任意のbool (C::*)(T const &) const
で専門のインスタンス化の試行をコンパイルに失敗します。引数と
C & obj, T const & var
と他:
また、IはMyClass<T,C>::callFirst
とMyClass<T,C>::callSecond
のそれぞれのための2つのオーバーロード、引数を有するものに設けられた第二なけれ
C const & obj, T const & var
を、いずれか呼び出そうMyClass<T,C>::callFirst
またはMyClass<T,C>::callSecond
のobj
(constの場合)は、 コンパイルに失敗します。あなたが追加することができ、このソリューションをデモするためのプログラムのために
:
#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'はおそらく無限集合の任意のクラスに依存しないので、おそらく 'std :: bind'と' std :: function'で終わるでしょう。 – MSalters