問題は、との両方function<int()>
がfunction<int(int)>
同じ関数から構築可能であるということです。std::function
VS2010でのコンストラクター宣言は次のようになります。
template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
SFINAEの部分を無視すると、ほとんど何からでも構築できます。型消去
std::/boost::function
と呼ばれる手法を使用して、呼び出されたときに署名を満たす限り、任意のオブジェクト/関数を渡すことができます。それからの1つの欠点は、コンストラクターではなく、署名が望むように呼び出すことができないオブジェクトを提供するときに、実装の最も深い部分(保存された関数が呼び出される場所)でエラーが発生することです。
この小さなクラスで問題を説明できます。
template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};
これで、コンパイラがオーバーロードセットの有効な関数を検索するときに、完全に適合する関数が存在しない場合、引数を変換しようとします。変換は、関数のパラメーターのコンストラクターを介して、または関数に与えられた引数の変換演算子を介して行うことができます。私たちの場合、それは前者です。
コンパイラは、の最初のオーバーロードを試行しa
ます。それを実行可能にするには、変換を行う必要があります。をに変換するためint(*)()
にmyfunc<int()>
、のコンストラクタを試しmyfunc
ます。何でも取るテンプレートなので、変換は自然に成功します。
次に、2番目のオーバーロードで同じことを試みます。コンストラクターは同じであり、与えられたものをすべて取得しているため、変換も機能します。
オーバーロードセットに2つの関数が残っているため、コンパイラは悲しいパンダであり、何をすべきかわからないため、呼び出しがあいまいであると単純に言います。
したがって、最終的にSignature
、テンプレートの一部は、宣言/定義を作成するときは型に属しますが、オブジェクトを作成するときはそうではありません。
編集:
タイトルの質問に答えることにすべての注意を払って、私はあなたの2番目の質問を完全に忘れました。:(
それを回避することはできますか、それとも(迷惑な)明示的なキャストを維持する必要がありますか?
Afaik、3つのオプションがあります。
- キャストを維持する
適切なタイプのfunction
オブジェクトを作成し、それを渡します
function<int()> fx = x;
function<int(int)> fy = y;
a(fx);
a(fy);
関数内の面倒なキャストを非表示にし、TMPを使用して適切な署名を取得します
TMP(テンプレートメタプログラミング)バージョンは非常に冗長でボイラープレートコードを備えていますが、キャストをクライアントから隠します。サンプルバージョンはここにあります。これは、関数ポインター型に部分的に特化したメタ関数に依存していget_signature
ます(C ++でパターンマッチングがどのように機能するかについての良い例を提供します)。
template<class F>
struct get_signature;
template<class R>
struct get_signature<R(*)()>{
typedef R type();
};
template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};
もちろん、これはサポートしたい引数の数に合わせて拡張する必要がありますが、それは一度実行されてから"get_signature.h"
ヘッダーに埋め込まれます。:)
私が検討しているがすぐに破棄されるもう1つのオプションは、TMPバージョンよりもさらに多くのボイラープレートコードを導入するSFINAEでした。
だから、ええ、それは私が知っているオプションです。それらの1つがあなたのために働くことを願っています。:)