2つの「C」関数があります。
_void fooA(const char*);
void fooW(const wchar_t*);
_
次に、ラッパーテンプレート関数があります。
_template<typename _TChar>
void foo(const _TChar* str)
{
// call fooA or fooB based on actual type of _TChar
// std::conditional .. ?
// fooA(str);
// fooW(str);
}
_
呼び出し元がfoo("Abc")
を呼び出す場合、このテンプレート関数はコンパイル時にfooA
を呼び出す必要があります。同様に、foo(L"Abc")
はfooW
の最後の呼び出しを行う必要があります。
それ、どうやったら出来るの? _std::conditional
_の使用を考えましたが、作成できませんでした。
fooA
またはfooB
はC関数なので、オーバーロードできません。
allあなたのwchar_t
クラステンプレート内のバージョン。たとえば、次のようにoverloads
とそのchar
対応する特殊化で対応します。
template<typename WideCharVersion>
struct overloads
{
void foo(wchar_t const * arg)
{
FooW(arg);
}
//more wchar_t functions
};
template<>
struct overloads<std::false_type>
{
void foo(char const * arg)
{
FooA(arg);
}
//more char functions
};
//a friendly alias!
template<typename T>
using is_wide_char = typename std::is_same<whar_t, T>::type;
そして、あなたはそれらを次のように使うことができます:
template<typename _TChar>
void foo(const _TChar* str)
{
overloads<is_wide_char<_TChar>>::foo(str);
}
他の方法は Expression SFINAE を使用することです。これはoverloads
のようなものを書く必要がなく、より少ないコードで同じ仕事をします:
template<typename _TChar>
void foo(const _TChar* str)
{
invokeOne(fooA, fooW, str);
}
次に、invokeOne
を次のように実装できます。
template<typename F1, typename F2, typename ... Args>
auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f1(args...))
{
return f1(args...);
}
template<typename F1, typename F2, typename ... Args>
auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f2(args...))
{
return f2(args...);
}
オンラインデモ をご覧ください。
このアプローチでは、overloads
クラステンプレートおよびにオーバーロードを追加して、特殊化する必要はありません。代わりに、それらを引数としてinvokeOne
に渡すだけで、適切なオーバーロードが呼び出されます。
お役に立てば幸いです。
次に、別の関数をオーバーロードします。私はfoo
がより多くの作業を行い、テンプレートである必要があると想定しています。次に、そのように定義されたfoo_forward_call
を呼び出します。
void foo_forward_call(char const* ptr) {
FooA(ptr);
}
void foo_forward_call(wchar_t const* ptr) {
FooW(ptr);
}
そして呼び出しサイトで:
template<typename _TChar>
void foo(const _TChar* str)
{
foo_forward_call(str);
}
c ++ 1zではconstexpr if
を使用できますが、正直なところ、オーバーロードされたソリューションの方が読みやすいと思います。
template<typename _TChar>
void foo(const _TChar* str)
{
if constexpr(std::is_same<_TChar, char>::value) {
FooA(str);
} else {
FooW(str);
}
}
または、Boost.Hana
の- overload
を使用できます。
template<typename _TChar>
void foo(const _TChar* str)
{
hana::overload(fooA, fooW)(str);
}
ちなみに、プログラムではアンダースコアの大文字の名前を使用しないでください。これらは、あらゆる用途(マクロなど)の実装用に予約されており、厄介な名前の衝突につながる可能性があります。
これは、テンプレートに関しては非常に奇妙なことのようです。代わりに通常のオーバーロードを使用することをお勧めします。
void foo(const char* p) { fooA(p); }
void foo(const wchar_t* p) { fooW(p); }
テンプレートの使用を主張する場合は、次のようにすることができます。
template <typename T>
void foo(const T* p)
{
// Declare functions here so that calling fooW with const char*
// and 'calling' fooA with const wchar_t* would not cause compile error.
void fooA(const T*);
void fooW(const T*);
if (std::is_same<char, T>::value)
fooA(p);
else
fooW(p);
}
私は一般的に問題を解決するのが好きです。それで、ものをオーバーロードするメカニズムを設計しましょう。
_overload_t<...>
_は、_...
_の呼び出し可能オブジェクトのセットを受け取り、operator()
の継承を介して、標準のオーバーロード解決を使用してそれらを選択するオブジェクトを生成します。
_template<class...Fs>
struct overload_t;
// the case where we have a function object:
template<class F>
struct overload_t<F>:F{
overload_t(F f):F(std::move(f)){}
using F::operator();
// boilerplate to ensure these are enabled if possible:
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// we cannot inherit from a function pointer. So
// store one, and write an `operator()` that forwards to it:
template<class R, class...Args>
struct overload_t<R(*)(Args...)>{
using F=R(*)(Args...);
F f;
overload_t(F fin):f(fin){}
R operator()(Args...args)const{
return f(std::forward<Args>(args)...);
}
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// the case where we have more than type to overload.
// recursively inherit from the one-arg and the rest-of-arg
// and using operator() to bring both of their () into equal standing:
template<class F0, class...Fs>
struct overload_t<F0,Fs...>:
overload_t<F0>,
overload_t<Fs...>
{
using overload_t<F0>::operator();
using overload_t<Fs...>::operator();
overload_t(F0 f0, Fs...fs):
overload_t<F0>(std::move(f0)),
overload_t<Fs...>(std::move(fs)...)
{}
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// a helper function to create an overload set without
// having to specify types. Will be obsolete in C++17:
template<class...Fs>
overload_t<Fs...> overload(Fs...fs){ return {std::move(fs)...};}
_
次に、複数のオーバーロードである単一のオブジェクトを生成するには、次のようにします。
_overload(FooA,FooW)( str );
_
そして、str
は通常のオーバーロード解決ルールに基づいてどちらかにディスパッチされます。これは他の場所でも役立ちます。そのため、書く価値があるので、使用時のコードは自己文書化されています。
実例 (すごい、最初は正しいと書きました!)
上記の_overload_t
_に追加できる改善点がいくつかあります。
構築中およびヘルパー関数でのf
sの完全な転送。
線形ではなくバランスのとれたバイナリツリーの継承(いくつかのオーバーロードが行われることが重要です)。これは、特に多数の関数の場合、ランタイムとコンパイル時のパフォーマンスに影響を与える可能性があります。
着信Fs
をイントロスペクトします;それらが_overload_t
_の場合、結合されたツリーのバランスを取ります。
C++ 17では、関数ポインターを受け取り、それをステートレスに呼び出す関数オブジェクトを返す_func<auto>
_テンプレート。コンパイラーは、関数ポインターを除外するのが比較的得意ですが、それらを変更する可能性のある実行時の状態がない場合は、より優れています。
_overload_t<>
_の処理方法の決定。現在、コンパイルに失敗しています。たぶん、それは空の_struct {}
_、あるいは呼び出し不可能なoperator()
を持つ構造体であるべきです。
_boost::hana::overload
_などの既存のライブラリを調べて、どのような違いがあるかを確認します。
呼び出されるオーバーロードを抽出する機能を、おそらくstatic tag_t<F> which_overload_helper( Args... ) const
メソッドとtemplate<class...Args> using which_overload = typename decltype( which_overload_helper( std::declval<Args>()... ) )::type;
を介して公開します。
着信Fs
sの一部にconst
operator()
がない場合に、オーバーロードを適切に選択します。関数ポインターはconst
、volatile
、operator()
の両方を持っている必要がありますか? 4つ全部? _&&
_と_&
_のオーバーロードはどうですか?
対象となる関数が多かったり、オーバーロードだけが解決しようとしていた問題ではない場合は、テンプレートを使用してこれを行う価値があるかもしれません。
ただし、この場合は、単にオーバーロードを記述します。
void foo(const char* x) { fooA(x); }
void foo(const wchar_t* x) { fooW(x); }
いくつかのオプションがあります。
明示的に特化したヘルパーを使用するstruct
:
template <typename>
struct helper;
template<>
struct helper<char>
{
void operator()(const char* x){ FooA(x); }
};
template<>
struct helper<wchar_t>
{
void operator()(const wchar_t* x){ FooW(x); }
};
template <typename _TChar>
void foo(const _TChar* str)
{
helper<_TChar>{}(str);
}
「静的if」実装を使用します(例 boost :: hana :: eval_if または my own ):
template <typename _TChar>
void foo(const _TChar* str)
{
vrm::core::static_if(std::is_same<_TChar, char>{})
.then([](const auto* x_str){ FooA(x_str); })
.else_([](const auto* x_str){ FooW(x_str); })(str);
}
ヘルパーオーバーロード関数を使用します。
void helper(const char* x) { FooA(x); }
void helper(const wchar_t* x) { FooW(x); }
template <typename _TChar>
void foo(const _TChar* str)
{
helper(str);
}