C言語やC++のコードを書いていると以下のようなコンパイル・エラーが発生することがある。
No matching function for call to 'fn'
これは関数呼び出しが正常に行えないという趣旨のメッセージで、原因はいくつか考えられる。
- # 関数(通常の関数呼び出しの場合)
- # テンプレート関数(C++のテンプレート利用時の場合)
関数
引数の型が一致しないケース
引数型が合っていないと呼び出せないのでエラーになる。
void fn(int v) {}
fn(99); // OK
fn(""); // No matching function for call to 'fn'
関数側の定義をきちんと確認せよ。
引数の個数が間違ってるケース
引数の数を合わせないとエラーになる。
void fn(int v) {}
fn(1); // OK
fn(1, 2); // No matching function for call to 'fn'
int sum(int a, int b) { return a + b; }
sum(1, 2); // OK
sum(1); // No matching function for call to 'sum'
ポインタ渡しの方法が間違ってるケース
void fn(int *p) {}
int v;
fn(&v); // OK
fn(v); // No matching function for call to 'fn'
fn(*v); // Indirection requires pointer operand ('int' invalid)
void fn(int *p) {}
int *p;
fn(p); // OK
fn(&p); // No matching function for call to 'fn'
fn(*p); // No matching function for call to 'fn'
同名関数のケース
C++のクラスや名前空間の中で、既存の関数と同名の関数を定義してしまった場合に本エラーが発生することがある。
この場合は名前解決演算子::
で、グローバルスコープ側のfn
関数を呼び出していることを明示してあげれば良い(::fn(88);
)。
void fn(int v) {}
namespace parent {
// パターン1
void fn() {
fn(99); // No matching function for call to 'fn'
::fn(88); // OK
}
// パターン2
void fn(int v) {}
namespace child {
void fn() {
fn(77); // No matching function for call to 'fn'
::fn(66); // OK
parent::fn(55); // OK
}
}
}
テンプレート関数
C++ではtypename T::type_name
という形式でTクラス内部の型を取得することが出来ますが、取得した型の使いようによっては型の不一致でエラーが発生してしまいます。
template <class T>void setter(T& obj, typename T::value_type val) {}
auto a = ...;
auto b = ...;
setter(a, b); // No matching function for call to 'setter'
setter関数呼び出し時にエラーが発生しています。
原因1
エラーになるケースは複数ありますが、一つはテンプレート引数T
に指定されたクラスから依存型名value_type
の名前解決が行えない問題で、こちらは対象クラスの側でvalue_typeをきちんと定義すれば問題ありません(using value_type = char;
or typedef char value_type;
)。
原因2
もう一つはT
に指定されたクラスの依存型名value_type
の型が確定していない状態でsetter
関数を利用するケースや、依存型名の実態先が異なるケース(NG:setter(A(), B().get())
, OK:setter(A(), A().get())
)で、このエラーが稀に発生します。Tに指定されたクラスがテンプレートクラスだったり、またはクラスが他の異なるテンプレート型に依存していたりすると、このようなエラーが発生する場合があります。
対処1
テンプレート型を明示することで対処出来る可能性があります。
setter<That>(a, b);
対処2
最終的には以下の方法でも対処が可能です。
template <class T, class U = typename T::value_type>
void setter(T& par, U val) {}
T::value_typeの型が既知の型であることを前提としています。また暗黙の型変換にも十分に注意する必要があります。
またSFINAEの仕組みを活用した対応方法もあるため、以下の節も参考にしてみてください。
SFINAE
今回のケースでテンプレート関連のエラーメッセージが表示されない理由にはC++のスフィネェ(SFINAE - Substitution Failure Is Not An Error)という言語仕様が関係しています。
SFINAEはテンプレートの置き換えが失敗してもそれ自体をエラーとは見なさずに、次のオーバーロード候補を探しに行くという特徴があります。
今回のケースでは候補となる次の関数が見つからないためにNo matching function for call
という文言のエラーが発生してしまっている可能性が考えられます。
以下のようにオーバーロード候補となる新たな関数を追加することで対処出来ます。
template <class T>
void setter(T& obj, typename T::value_type val) {
obj.setInteger(val);
}
template <class T>
void setter(T& par, typename T::float_type val) {
obj.setFloat(val);
}