2016-03-25 61 views
0

基於an answer from Nawaz我想使用enable_if來確定模板參數是否是一個容器,如果它是我想顯示類型名稱的自定義消息,而不是來自typeid的名字。我已經通過兩種方式實現了模板專業化。代碼編譯並運行,但在任何情況下都不會調用專用方法。我假設我錯誤地使用了enable_if,這裏的正確應用是什麼? (a)需要的包含文件(b)準備模板代碼(使用SFINAE)(c)假定結構的兩個實現執行任務(d)一些客戶端代碼std :: enable_if在模板參數來確定STL容器

#include <typeinfo> 
#include <string> 
#include <list> 
#include <vector> 
#include <iostream> 
using namespace std; 

template<typename T> 
struct has_const_iterator 
{ 
private: 
    typedef char      yes; 
    typedef struct { char array[2]; } no; 

    template<typename C> static yes test(typename C::const_iterator*); 
    template<typename C> static no test(...); 
public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
    typedef T type; 
}; 

template <typename T> 
struct has_begin_end 
{ 
    template<typename C> static char(&f(typename std::enable_if< 
     std::is_same<static_cast<typename C::const_iterator(C::*)() const>(&C::begin), 
     typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

    template<typename C> static char(&f(...))[2]; 

    template<typename C> static char(&g(typename std::enable_if< 
     std::is_same<static_cast<typename C::const_iterator(C::*)() const>(&C::end), 
     typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

    template<typename C> static char(&g(...))[2]; 

    static bool const beg_value = sizeof(f<T>(0)) == 1; 
    static bool const end_value = sizeof(g<T>(0)) == 1; 
}; 
template<typename T> 
struct is_container : std::integral_constant<bool, 
    has_const_iterator<T>::value && 
    has_begin_end<T>::beg_value && 
    has_begin_end<T>::end_value> 
{ }; 

struct TypeName { 
    template <typename T> 
    static const char* get() { 
     return typeid(T).name(); 
    } 

    template <typename T, typename std::enable_if<is_container<T>::value>::type > 
    static const char* get() 
    { 
     typedef typename T::value_type ElementType; 
     std:string containerType = ""; 
     if (std::is_same<decltype(std::vector<ElementType>), T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<decltype(std::list<ElementType>), T>::value) { 
      containerType = "(List) "; 
     } 
     std::string returnString = "Container " + containerType; 
     returnString += " of "; 
     returnString += get<ElementType>(); 
     return returnString.c_str(); 
    } 
}; 

template <typename T> struct GypeName { 

    static const char* get() { 
     return typeid(T).name(); 
    } 

    template <class = typename std::enable_if<is_container<T>::value>::type > 
    static const char* get() 
    { 
     typedef typename T::value_type ElementType; 
     std:string containerType = ""; 
     if (std::is_same<decltype(std::vector<ElementType>), T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<decltype(std::list<ElementType>), T>::value) { 
      containerType = "(List) "; 
     } 
     std::string returnString = "Container " + containerType; 
     returnString += " of "; 
     returnString += GypeName<ElementType>::get(); 
     return returnString.c_str(); 
    } 
}; 



int main(int argc, char** argv) { 
    cout << is_container<int>::value << endl; 
    cout << is_container<std::vector<int>>::value << endl; 
    cout << TypeName::get<int>() << endl; 
    cout << TypeName::get<std::string>() << endl; 
    cout << TypeName::get<std::vector<int>>() << endl; 
    cout << TypeName::get<std::vector<std::vector<int>>>() << endl; 
    cout << GypeName<int>::get() << endl; 
    cout << GypeName<std::string>::get() << endl; 
    cout << GypeName<std::vector<int>>::get() << endl; 
    cout << GypeName<std::vector<std::vector<int>>>::get() << endl; 
    return 0; 
} 

這一切的輸出

0 
1 
int 
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > 
class std::vector<int,class std::allocator<int> > 
class std::vector<class std::vector<int,class std::allocator<int> >,class std::allocator<class std::vector<int,class std::allocator<int> > > > 
int 
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > 
class std::vector<int,class std::allocator<int> > 
class std::vector<class std::vector<int,class std::allocator<int> >,class std::allocator<class std::vector<int,class std::allocator<int> > > > 

不調用在這兩種情況下的特殊功能。

+0

有在提供的代碼拼寫錯誤/編譯器錯誤,[演示](http://coliru.stacked-crooked.com/a/774f27cddb6363be)一旦修復,(但UB不固定(從臨時返回const char *))。 – Jarod42

+0

該代碼需要boost,並使用C++ 11(visual studio 2015) - 我將刪除引用並提供一些自包含的內容 –

+0

好的,主要問題是enable_if應該位於返回類型而不是模板參數。謝謝Jarod42。 –

回答

1

您可以使用以下方法:

struct TypeName { 
    template <typename T> 
    static 
    std::enable_if_t<!is_container<T>::value, const char*> 
    get() { 
     return typeid(T).name(); 
    } 

    template <typename T> 
    static 
    std::enable_if_t<is_container<T>::value, std::string> 
    get() 
    { 
     typedef typename T::value_type ElementType; 
     std::string containerType = ""; 
     if (std::is_same<std::vector<ElementType>, T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<std::list<ElementType>, T>::value) { 
      containerType = "(List) "; 
     } 
     return (boost::format("Container %s of %s") 
       % containerType 
       % TypeName::get<ElementType>()).str(); 
    } 
}; 

Demo

注意std::string被視爲char的容器。
當你有特定的(運行時間:()情況下的矢量/列表中,您可能只需要使用專門爲這兩個替代:

namespace detail 
{ 
    template <typename T> struct TypeName 
    { 
     auto operator()() const { return typeid(T).name(); } 
    }; 

    template <template <typename...>class C, typename T, typename...Ts> 
    struct TypeName<C<T, Ts...>> 
    { 
     auto operator()() const { 
      return (boost::format("container of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

    template <typename T, typename A> 
    struct TypeName<std::vector<T, A>> 
    { 
     auto operator()() const { 
      return (boost::format("Vector of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

    template <typename T, typename A> 
    struct TypeName<std::list<T, A>> 
    { 
     auto operator()() const { 
      return (boost::format("List of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

} 

struct TypeName { 
    template <typename T> 
    static auto get() { 
     return detail::template TypeName<T>{}(); 
    } 
}; 

Demo

相關問題