同じ基本機能を実装するが、異なるメンバ関数名を持つインターフェイスを持つ複数のクラスに対して再利用したい汎用コードがあるとします。たとえば、基底クラスがerase
メンバ関数を持つ場合、次のコードが動作します。 std::set
またはstd::unordered_set
。コンパイル時にC++でメンバ関数のエイリアス
template <typename T>
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) {
T set;
std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
start = std::chrono::high_resolution_clock::now();
set.erase(v);
end = std::chrono::high_resolution_clock::now();
return end - start;
}
ただし、この機能を、代わりにunsafe_erase
という名前の関数が提供されています。
私の最初のアプローチは、部分テンプレートの特殊化を使用して、以下を定義し、代わりにset_ops<T>::erase(set, v)
を呼び出すことによって、型特性を利用することでした。残念ながら、これはコンパイルされません。 tbb::concurrent_unordered_set
はテンプレート型であり、型ではないからです。私はまた、キー型の2番目のテンプレート引数で型の特性を拡張しようとしましたが、T
がstd::mem_fn(&T<U>::erase)
のテンプレートではないため、コンパイルが失敗します。
template <typename T>
struct set_ops {
constexpr static auto erase = std::mem_fn(&T::erase);
};
template <>
struct set_ops<tbb::concurrent_unordered_set> {
constexpr static auto erase = std::mem_fn(&T::unsafe_erase);
};
また、メンバー関数を次のように関数テンプレートでラップしようとしました。これはコンパイルされているように見えますが、例えません。 decltype ((({parm#1}.erase)({parm#2})),((bool)())) erase<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >(std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >&, std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >::key_type const&)
template <typename T>
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.erase(v), bool());
template <typename T>
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.unsafe_erase(v), bool());
どのように私は、コンパイル時にこのエイリアシングを実行する必要がありますか?私は、基になる各クラスの抽象インタフェースを継承する実装を提供することも、メンバ関数へのポインタを利用することもできますが、実行時のオーバーヘッドを避けたいと思います。
は不必要に複雑に思えます。 'erase'の2つのバージョンは、過負荷解決によって選択されるフリー関数です。 – MSalters
@MSaltersこれはetherの部分的な特殊化または 'enable_if' SFINEのトリックです。さもなければ、 'erase(tbb :: concurrent_unordered_set&const tbb :: concurrent_unordered_set :: value_type&)'のあいまいなオーバーロードが発生します。 –