2017-09-24 10 views
1

メンバーメソッドを特化しようとしています。
この前の質問を読む:std::enable_if to conditionally compile a member function
私は間違っていることをよく理解できます。メンバーの部分的な特殊化

#include <string> 
#include <iostream> 
#include <type_traits> 

template<typename T> 
class Traits 
{ 
}; 

struct Printer 
{ 
    template<typename T> 
    typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type 
    operator()(T const& object) 
    { 
     std::cout << object; 
    } 
    template<typename T> 
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type 
    operator()(T const& object) 
    { 
     std::cout << Traits<T>::converter(object); 
    } 
}; 

template<> 
class Traits<std::string> 
{ 
    public: 
     static std::size_t converter(std::string const& object) 
     { 
      return object.size(); 
     } 
}; 

int main() 
{ 
    using namespace std::string_literals; 

    Printer  p; 

    p(5); 
    p("This is a C-string"); 
    p("This is a C++String"s); // This compiles. 
} 

コンパイルは与える:

> g++ -std=c++1z X.cpp 
X.cpp:42:5: error: no matching function for call to object of type 'Printer' 
    p(5); 
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>' 
    operator()(T const& object) 
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>' 
    operator()(T const& object) 
    ^

彼らの両方が、彼らは方法converterを見ることができないので、失敗しているようです。しかし、SFINEとstd::enable_ifを使用して、この関数が存在しないことを認識し、メソッドの1つだけをインスタンス化するようにしています。

同じエラーが種類毎に生成される:

X.cpp:43:5: error: no matching function for call to object of type 'Printer' 
    p("This is a C-string"); 
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>' 
    operator()(T const& object) 
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>' 
    operator()(T const& object) 
    ^

注:それはstd::stringバージョンにコンパイルします。

答えて

0

non-special traitsに非機能converterを追加することはどうですか?

template<typename T> 
class Traits 
{ 
    public: enum class Dummy{nothing}; 
    public: static Dummy const converter = Dummy::nothing; 
}; 

Run this code online

1

あなたは民間のヘルパー関数に延期、積極SFINAE-Dの過負荷を好むようにオーバーロードの解決を使用することができます - と負SFINAE-dの1持っていません。

struct Printer 
{ 
    template <class T> 
    void operator()(T const& object) { 
     call_impl(object, 0); 
    } 

private: 
    // selected if Traits<T>::converter exists and is a function 
    // preferred in this case because int is better than ... 
    template<typename T> 
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type 
    call_impl(T const& object, int) 
    { 
     std::cout << Traits<T>::converter(object); 
    } 

    // selected if either Traits<T>::converter doesn't exist or isn't a function 
    template<typename T> 
    void call_impl(T const& object, ...) 
    { 
     std::cout << object; 
    } 

}; 

制限機能を持つC++ 2aで得られる素晴らしい利点の1つは、追加のヘルパーなしでこれを実行できることです。

struct Printer 
{ 
    template <class T> 
     requires std::is_function<decltype(Traits<T>::converter)>::value 
    void operator()(T const& object) 
    { 
     std::cout << Traits<T>::converter(object); 
    } 

    template <class T> 
    void operator()(T const& object) 
    { 
     std::cout << object; 
    } 
}; 
0

問題はどのようにSFINAEが問題になりますか。置換に失敗すると、関数全体がプログラムから取り出されます。だから、あなたの述語typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::typefalseのケースをキャッチすることになっていても、converterが存在しないと、テーブルから過負荷が取り除かれます。基本的には

struct Printer 
{ 
    template<typename T> 
    void 
    impl(T const& object, ...) 
    { 
     std::cout << object; 
    } 

    template<typename T> 
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type 
    impl(T const& object, void*) 
    { 
     std::cout << Traits<T>::converter(object); 
    } 

    template<typename T> 
    void 
    operator()(T const& x) 
    { 
     return impl(x, nullptr); 
    } 
}; 

最も簡単な回避策は、このようなものであるあなたは常に述語を使用せずに動作するコンパイラの何かを与えます。ここでのトリックは、が...の代わりにvoid*にマッチするので、あなたが望むことができるということです。

戻り値の型がtrue_typeまたはfalse_typehas_converter関数を作成し、その実装にオーバーロードさせることができます。

struct Printer 
{ 
    template<typename T> 
    std::false_type 
    has_converter(T const& object, ...); 

    template<typename T> 
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, std::true_type>::type 
    has_converter(T const& object, void*); 

    template<typename T> 
    void impl(T const& x, std::false_type) 
    { 
     std::cout << x; 
    } 

    template<typename T> 
    void impl(T const& x, std::true_type) 
    { 
     std::cout << Traits<T>::converter(x); 
    } 

    template<typename T> 
    void 
    operator()(T const& x) 
    { 
     return impl(x, decltype(has_converter(x, nullptr))()); 
    } 
}; 

一つは、(上記と同じ手法を使用)でも簡単に、このプロパティを使用して作成するヘルパー関数やテンプレートconstexpr boolを想像することができます。

template <typename T> 
constexpr bool has_converter = ???; 
関連する問題