2012-07-19 52 views
6

我有以下問題:C++如何模板之間的區別集裝箱和原生型

template<class T> 
void set(std::string path, const T data) 
{ 
    stringstream ss; 
    ss << data << std::endl; 
    write(path, ss.str(); 
} 

template<class T> 
void set(std::string path, const T data) 
{ 
    std::stringstream ss; 
    for(typename T::const_iterator it = data.begin(); it < data.end(); ++it) 
    { 
     ss << *it; 
     if(it < data.end() -1) 
      ss << ", "; 
    } 
    ss << std::endl; 
    write(path, ss.str()); 
} 

我收到以下錯誤:

error: ‘template<class T> void myclass::set(std::string, T)’ cannot be overloaded 
error: with ‘template<class T> void myclass::set(std::string, T)’ 

是有辦法的容器類型和區分模板中的其他類型?

+0

型特徵。看起來像你需要使自己的is_container特質(http://stackoverflow.com/questions/7617203/is-it-possible-to-use-type-traits-to-check-whether-a-type-is-a -容器)。 (之前沒有做過,只是等待構建完成,所以沒有一個完整的答案 - 抱歉。但我會對解決方案感興趣;)) – peterchen 2012-07-19 09:40:20

+0

你想'const T&data',和'ss << data'嗎? – 2012-07-19 09:40:46

+0

這幾乎是一個很好的問題,但是有一些語法錯誤使得回答比需要的更困難。 ('T'應該是'set'的第一行的第三行的'data',而您對'write'的調用缺少''',但在添加之後仍然錯誤)。 – Flexo 2012-07-19 09:48:05

回答

6

在C++中03,你可以用SFINAE一點點這樣做有選擇性地啓用不同的版本爲不同類型的功能:

#include <boost/type_traits.hpp> 
#include <sstream> 
#include <iostream> 
#include <vector> 

using namespace std; 

template<class T> 
void set(typename boost::enable_if<boost::is_pod<T>, std::string>::type path, const T data) 
{ 
    std::cout << "POD" << std::endl; 
    stringstream ss; 
    ss << data << std::endl; 
} 

template<class T> 
void set(typename boost::disable_if<boost::is_pod<T>, std::string>::type path, const T data) 
{ 
    std::cout << "Non-POD" << std::endl; 
    std::stringstream ss; 
    for(typename T::const_iterator it = data.begin(); it < data.end(); ++it) 
    { 
     ss << *it; 
     if(it < data.end() -1) 
      ss << ", "; 
    } 
    ss << std::endl; 
} 

int main() { 
    int i; 
    float f; 
    std::vector<int> v; 
    set("", v); 
    set("", i); 
    set("", f); 
} 

我用升壓爲方便起見,這裏,但你可以滾你如果boost不是一個選項,或者使用C++ 11來代替它,那麼就擁有它。

is_pod是不太你想真的是,你可能想要一個is_container特質,但that's not so trivial, you'll need to make a trait of your ownis_pod使得如何使用特性來選擇性地啓用功能的簡單回答一個很好的近似。

8

使用特點:

#include <type_traits> 

template <typename T> 
typename std::enable_if<is_container<T>::value>::type 
set (std::string const & path, T const & container) 
{ 
    // for (auto const & x : container) // ... 
} 


template <typename T> 
typename std::enable_if<!is_container<T>::value>::type 
set (std::string const & path, T const & data) 
{ 
    std::ostringstream oss; 
    oss << data; 
    write(path, oss.str()); 
} 

您可以找到pretty printer code合適的特質。

0

正如我的前任寫道,你必須使用某種特質。你或許應該使用升壓對於這一點,但如果你不希望你可以使用這樣的事情(http://ideone.com/7mAiB):

template <typename T> 
struct has_const_iterator { 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename C> static yes& test(typename C::const_iterator*); 
    template <typename> static no& test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

template <bool> class bool2class {}; 

template <class T> 
void set_inner(const std::string &path, T & var, bool2class<false> *) { 
     // T is probably not STL container 
} 

template <class T> 
void set_inner(const std::string &path, T & var, bool2class<true> *) { 
     // T is STL container 
} 

template <class T> 
void set(const std::string &path, T &var) { 
     set_inner(path, var, (bool2class<has_const_iterator<T>::value>*)0); 
} 

這不是一件容易的事,從簡單的數組區分容器,所以我這裏使用的檢查類型是否有const_iterator。也許你還應該檢查它是否有begin()end()和其他你將在未來的代碼中使用的東西。

1

您可以嘗試替換失敗不是錯誤(SFINAE)技術。

首先,需要一個函數來確定類型是否具有一個迭代構件...

template <typename T> 
struct Has_Iterator 
{ 
    template <typename> 
    static char test(...); 

    template <typename U> 
    static int test(typename U::const_iterator*); 

    static const bool result = sizeof test<T>(0) != sizeof(char); 
}; 

在上面的代碼,C++標準要求優先於模糊使用test(typename U::const_iterator*)」 .. 。「參數匹配,只要U實際上是一個帶有const_iterator成員類型的結構/類。否則,你有一個「替代失敗」 - 這不是一個致命的錯誤停止編譯(因此SFINAE),並試圖找到匹配的功能是由test(...)滿足。由於兩種功能的返回類型不同,因此sizeof操作員可以測試哪一個匹配,並適當設置result布爾值。

然後你就可以將請求轉發給打印的東西到支持它們的模板特...

template <typename T> 
void print(const T& data) 
{ 
    printer<Has_Iterator<T>::result, T>()(data); 
} 

// general case handles types having iterators... 
template <bool Has_It, typename T> 
struct printer 
{ 
    void operator()(const T& data) 
    { 
     for (typename T::const_iterator i = data.begin(); i != data.end(); ++i) 
      std::cout << *i << ' '; 
     std::cout << '\n'; 
    } 
}; 

// specialisation for types lacking iterators... 
template <typename T> 
struct printer<false, T> 
{ 
    void operator()(const T& data) 
    { 
     std::cout << data << '\n'; 
    } 
}; 
+0

Nitpick:*「sizeof'運算符可以測試哪一個被調用了」* - 'test()'是從來沒有真正叫過。我確信你已經知道了。 – cdhowie 2012-10-08 09:42:54

+0

@cdhowie:公平點 - 回答更新爲「匹配」。乾杯。 – 2012-10-09 00:04:45