2017-09-24 67 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來識別這個函數不存在,因此只實例化其中一個方法。

爲每種類型的產生同樣的錯誤:

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

將非功能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之一:

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中獲得的好處之一是我們可以在沒有額外幫助的情況下執行此操作:

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>::type意在抓住false的情況,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); 
    } 
}; 

基本上是:你給編譯器的東西,將永遠不使用謂詞的工作。這裏的訣竅是nullptr將與void*匹配,而不是...,因此它會按照您的要求進行操作。

如果你想獲得它真正的樂趣,你可以做一個has_converter函數返回類型爲true_typefalse_type和超負荷的實施。

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 = ???;