TL; DR非メンバ関数CreateProcessor<T>()
の存在に応じて異なる値に対して異なる動作をするテンプレート関数Process(T value)
を書きたいと思います。そのために私は何ができますか?SFINAE非メンバーテンプレート関数の存在を検出する
SFINAEに問題があります。あるタイプのタイプT
のために、インタフェースIProcessor<T>
の実装を返す関数CreateProcessor
をサポートする必要があるとします。
C++では、戻り値の型だけが異なる関数のオーバーロードを複数作成することはできません。したがって、CreateProcessor
もT
によってテンプレート関数の関数にする必要があります。
は今、それ以外の場合はエラーになる必要がある、すなわち、それが実装されている場合CreateProcessor<T>()
でプロセッサを使用してvalue
を処理する必要があり、我々は異なっCreateProcessor<T>()
の有無に応じて、作品テンプレート関数Process<T>(T value)
を書きたいとします。
私は、次のコードを記述しようとした:
#include <cstdio>
#include <type_traits>
// A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t.
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// An interface for a processor that receives a value of specific type.
template<class T>
class IProcessor {
public:
virtual void process(T value) = 0;
};
// A processor for int.
class IntProcessor : public IProcessor<int> {
public:
virtual void process(int value) override {
printf("IntProcessor::process is called for value = %d\n", value);
}
};
// Template prototype.
template<class T>
IProcessor<T>* CreateProcessor();
// Template specialization for int.
template<>
IProcessor<int>* CreateProcessor() {
return new IntProcessor();
}
// Detector of CreateProcessor.
template<class, class=void>
struct CreateProcessorImplemented : std::false_type { };
template<class T>
struct CreateProcessorImplemented<T, void_t<decltype(CreateProcessor<T>())>> : std::true_type { };
// Specializations depending on existence of CreateProcessor.
template <typename T>
typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) {
IProcessor<T>* processor = CreateProcessor<T>();
processor->process(value);
}
template <typename T>
typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) {
printf("Processor for requested typename is unavailable\n");
}
int main() {
Process(42);
Process("abc");
// static_assert(!CreateProcessorImplemented<char const*>::value, ":(");
/* This static_assert fails with an error:
* code.cpp:56:5: error: static assertion failed: :(
* static_assert(!CreateProcessorImplemented<char const*>::value, ":(");
*/
}
これはリンクエラーになりますが:
/tmp/ccTQRc9N.o:code.cpp:function std::enable_if<CreateProcessorImplemented<char const*, void>::value, void>::type Process<char const*>(char const*): error: undefined reference to 'IProcessor<char const*>* CreateProcessor<char const*>()'
collect2: error: ld returned 1 exit status
私の考えは、私たちがCreateProcessorImplemented<char const*>
を解決する場合がありますので、decltype(CreateProcessor<const char*>())
が失敗しないということですテンプレートのプロトタイプIProcessor<T> CreateProcessor()
とコンパイラはdecltypeが何とか論理的ですが必要なものではないIProcessor<T>
と等しいとみなします。
' CreateProcessorImplemented'は常に( 'のstd :: declval'を参照してください)'のstd :: true_type'に由来します。 – Simple
@シンプル、それは意味を作る。リンカーからの知識が必要なので、関数がコンパイル時に実装されているかどうかをチェックする方法はないと思いますよね? –
@WojciechFrohmbergからの回答です。非メンバ関数の代わりに静的メンバ関数を使って 'struct'を使います。 'struct 'が特殊化されていないため、静的メンバー関数がないかどうかを検出できます。それは基本的にタイプ特性です。 – Simple