🗂
C++14 で std::enable_if で fallback つきでクラスメソッドのオーバーロードしたいメモ
C++14 で, テンプレート変数を取るクラスのメソッドで, 型に応じて異なる振る舞いをしたいが, fallback(指定した型以外を処理するデフォルトメソッド) もほしい.
とりあえず std::enable_if
でいけるらしいが...
enable_if
で分けたいときが 3 個以上あるとき...(2 個であれば enable_if と enable_if<!> でいけるため)
テンプレートなんもわからん...
template<class T>
class Bora {
public:
// dependent type が必要なので, V をでっちあげる
template <typename V = T, std::enable_if_t<std::is_enum<V>::value, std::nullptr_t> = nullptr>
bool dora() {
std::cout << "enum type\n";
return true
}
bool dora() {
std::cout << "fallback\n";
return false;
}
}
のようにしても,
enum class Muda { Ari, Ora };
Bora<Muda> eb;
eb.dora(); // enum type を期待
Bora<int> ib;
ib.dora(); // fallback を期待
どちらも fallback のほうが呼ばれてしまいます.
fallback
fallback
解決
コメントで解決方法ご教示いただきました. ありがとうございます!
以下は元記事の記録用です.
とりあえずの解決1
ネットをあさってもあんまり情報ありません.
返り値が同じだとむりぽっそ?
返り値と引数を template 型にする必要がありますが, pxrUSD のコードで見つけた,
typename を返り値と引数で受け取るようにしてやるのがとりあえずの解決でしょうか...
// dependant type が必要なので, V をでっちあげるのは引き続き必要
template <typename V = T>
typename std::enable_if<std::is_enum<V>::value, T>::value
dora(T x) {
std::cout << "enum type\n";
return x;
}
// T dora(T x) だとエラーになるので注意
template <typename V = T>
V dora(V x) {
std::cout << "fallback\n";
return x;
}
とりあえずの解決2
にあるように, 判定をまとめるようにして, enable_if と enable_if<!> の二つになるようにする.
C++17
C++17 なら if constexpr
で解決できるでしょう.
Discussion
複数用意した候補について、一方の型の制約がもう一方を「含む」ような形になる場合はオーバーロードよりも特殊化を用いるほうがやりやすいです。 完全特殊化は部分特殊化より優先され、部分特殊化はプライマリテンプレートより優先されます。
fallback として使えるプライマリテンプレートには「否定」を指示する必要がない分だけ少し楽です。
ただし、関数テンプレートは部分特殊化は出来ないという言語仕様上の制限があるために補助的なクラスを使う必要があるのが回りくどいかもしれません。
ありがとうございます!
なんと!
struct 作る必要ありますが, メソッドのシグネチャは同じにできるのがいいですね.
C++20 以降ならテンプレートパラメタに直接に制約を付けられるのでややこしい SFINAE の規則を使わなくてよくなりました。 従来の規則がなくなったわけではないので組み合わさると難解だったりはしますが、単純な場合にはより制約が強いほうが優先されるという感覚的にわかりやすい規則ですし、見た目にも何をしたいのかが一目瞭然です。
C++20 を導入できない事情なども色々ある場合もあるかもしれませんが、 C++20 でだいぶん楽になることは多いので可能であれば新しい機能を使いたいところです。
はい. C++20 では std::format あるのも羨ましいところ.
今回は実務 OSS ライブラリのコードでの利用を想定していて, Python binding など考えると C++14(頑張れば C++17 いけるが)が限界なのですよね.
実務用途では, だいたい spec の年 + 7 ~ 10 年くらいたたないと mature かつ幅広く利用可能にならなそうな気がしています. つまり C++17 がようやく使えそうになり, C++20 が実務で使えるのは 2027 ~ 2030 年...
今回の件はコンパイル自体は通っていて、オーバーロード解決の優先順位の話だと思いますので、
ダミー引数を使って優先順位を操作する方法もあります。
(数値リテラルの解釈から
int
が優先される)参考
ありがとうございます!
ダミー引数必要ですが, スッキリ書けるのがいいですね. ダミー引数とってもいいようなケースではこちらで, メソッドシグネチャを変えたくないときは struct でのやり方でいきたいと思います.