2016-03-11 6 views
6

TL; DR非メンバ関数CreateProcessor<T>()の存在に応じて異なる値に対して異なる動作をするテンプレート関数Process(T value)を書きたいと思います。そのために私は何ができますか?SFINAE非メンバーテンプレート関数の存在を検出する

SFINAEに問題があります。あるタイプのタイプTのために、インタフェースIProcessor<T>の実装を返す関数CreateProcessorをサポートする必要があるとします。

C++では、戻り値の型だけが異なる関数のオーバーロードを複数作成することはできません。したがって、CreateProcessorTによってテンプレート関数の関数にする必要があります。

は今、それ以外の場合はエラーになる必要がある、すなわち、それが実装されている場合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>と等しいとみなします。

+0

' CreateProcessorImplemented'は常に( 'のstd :: declval'を参照してください)'のstd :: true_type'に由来します。 – Simple

+0

@シンプル、それは意味を作る。リンカーからの知識が必要なので、関数がコンパイル時に実装されているかどうかをチェックする方法はないと思いますよね? –

+0

@WojciechFrohmbergからの回答です。非メンバ関数の代わりに静的メンバ関数を使って 'struct'を使います。 'struct 'が特殊化されていないため、静的メンバー関数がないかどうかを検出できます。それは基本的にタイプ特性です。 – Simple

答えて

5

それを動作させるための一つの方法は、このようなCreateProcessorに機能するために、ラッパー構造体を使用することです:

#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> 
struct ProcessorCreator: std::false_type { 
    static IProcessor<T>* CreateProcessor(); 
}; 

// Template specialization for int. 
template<> 
struct ProcessorCreator<int>: std::true_type { 
static IProcessor<int>* CreateProcessor() { 
    return new IntProcessor(); 
} 
}; 

// Detector of CreateProcessor. 
template<class, class=void> 
struct CreateProcessorImplemented : std::false_type { }; 

template<class T> 
struct CreateProcessorImplemented<T, typename std::enable_if<ProcessorCreator<T>::value>::type > : 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 = ProcessorCreator<T>::CreateProcessor(); 
    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, ":("); 
*/ 
} 

代わりにテンプレート宣言を削除して、関数オーバーロードを使用してIProcessorテンプレートパラメータの型を渡すことができます - 仮引数を作成することによって、 :関数は、あなたの戻り値の型を伝えるために `decltype`の実装を持っている必要はありませんので、

#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); 
    } 
}; 


IProcessor<int>* CreateProcessor(const int&) { 
    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(std::declval<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(value); 
    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, ":("); 
*/ 
} 
関連する問題