オーバーロードされたメンバ関数の有無による条件分岐
メンバ関数の有無による条件分岐をしようと思って調べました。
それ自体はできたのですが、メンバ関数がオーバーロードされていると上手くいかなかったのでそれの解決策と実装例を載せたいと思います。
とりあえず、目標としてはイテレータを返り値とする find
メンバ関数の有無で分岐するような contains
関数を作りたいと思います。
結論
結論としては下記のような実装例になりました。
#include <bits/stdc++.h>
と using namespace std;
を使用しています(すいません)。
namespace helper { template <typename T> class has_iterator { template <typename Container> static true_type test(typename Container::iterator *); template <typename Container> static false_type test(...); public: static const bool value = decltype(test<T>(0))::value; }; template <typename Container, typename T> class has_find { template <typename InnerContainer, int dummy = (static_cast<typename enable_if<has_iterator<InnerContainer>::value, InnerContainer>::type::iterator (InnerContainer::*)(const T &)>(&InnerContainer::find), 0)> static true_type check(InnerContainer *); static false_type check(...); static Container *container; public: static const bool value = decltype(check(container))::value; }; } // namespace helper template <typename Container, typename T> bool contains(const Container &container, const T &x) { if constexpr (helper::has_find<Container, T>::value) { return container.find(x) != end(container); } else { return find(begin(container), end(container), x) != end(container); } }
#include <bits/stdc++.h> using namespace std; namespace helper { template <typename T> class has_iterator { template <typename Container> static true_type test(typename Container::iterator *); template <typename Container> static false_type test(...); public: static const bool value = decltype(test<T>(0))::value; }; template <typename Container, typename T> class has_find { template <typename InnerContainer, int dummy = (static_cast<typename enable_if<has_iterator<InnerContainer>::value, InnerContainer>::type::iterator (InnerContainer::*)(const T &)>(&InnerContainer::find), 0)> static true_type check(InnerContainer *); static false_type check(...); static Container *container; public: static const bool value = decltype(check(container))::value; }; } // namespace helper template <typename Container, typename T> bool contains(const Container &container, const T &x) { if constexpr (helper::has_find<Container, T>::value) { return container.find(x) != end(container); } else { return find(begin(container), end(container), x) != end(container); } } int main() { cout << boolalpha; { cout << "set-------------" << endl; set<int> s; s.insert(10); cout << contains(s, 10) << endl; cout << contains(s, 3) << endl; } { cout << "map-------------" << endl; map<int, int> mp; mp[2] = 3; cout << contains(mp, 3) << endl; cout << contains(mp, 2) << endl; } { cout << "vector-------------" << endl; vector<int> vs = { 1, 4, 3 }; cout << contains(vs, 5) << endl; cout << contains(vs, 4) << endl; cout << contains(vs, 1) << endl; } { // expect : string uses std::find cout << "string-------------" << endl; string s = "341"; cout << contains(s, '5') << endl; cout << contains(s, '4') << endl; cout << contains(s, '1') << endl; } }
実装例を目で追うときははhas_find
のvalue
に何が入るのかというところから追って行ってください。
解決策
オーバーロードされたメンバ関数を指定するにはstatic_cast
で型を指定すればよいです。
static_cast<返り値(関数ポインタ*)(引数)>(目的のメンバ関数のアドレス)
のようにします。
実装例では
static_cast<省略::iterator (InnerContainer::*)(const T &)>(&InnerContainer::find)
としています。
ここで、イテレータを持たない型が入ってきたときのためにenable_if
でhas_iterator<T>::value
がtrue
かfalse
かを判別しています。
返り値の型がtrue_type
になっているcheck
関数のテンプレート部分でエラーが起きると、返り値の型がfalse_type
のcheck
関数に移ります。
基本的な説明はこちらの記事がとても分かりやすかったです。
あとがき
オーバーロードされたメンバ関数を指定するためにはstatic_cast
で型を指定する必要があり、メンバ関数の有無を判定するものは全てには対応させられないのではないかと思います。(マクロならできるんですかね?)