2012-07-19 28 views
6

Tôi có vấn đề sau đây:C++ như thế nào để phân biệt giữa mẫu cho thùng chứa và nguồn gốc loại

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

tôi nhận được lỗi sau:

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

Có cách nào để phân biệt giữa các loại container và các loại khác trong mẫu?

+0

đặc điểm loại. Có vẻ như bạn cần tạo đặc điểm is_container của riêng mình (http://stackoverflow.com/questions/7617203/is-it-possible-to-use-type-traits-to-check-whether-a-type-is-a -thùng đựng hàng). (Đã không làm điều này trước đây, và chỉ cần chờ đợi cho một xây dựng để hoàn thành, do đó, không phải là một câu trả lời hoàn chỉnh - xin lỗi.Nhưng tôi sẽ quan tâm đến giải pháp;)) – peterchen

+0

Bạn có muốn 'const T & data', và' ss << data' ?? –

+0

Đây là * gần như * một câu hỏi hay, nhưng có một vài lỗi cú pháp khiến nó khó trả lời hơn là cần thiết. ('T' nên là' dữ liệu' trên dòng thứ ba của dạng đầu tiên của 'set' và các lệnh gọi' write' của bạn thiếu một ')' nhưng vẫn sai sau khi thêm vào đó). – Flexo

Trả lời

6

Trong C++ 03 bạn có thể làm điều này với một chút SFINAE chọn lọc cho phép phiên bản khác nhau của hàm với nhiều loại khác nhau:

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

Tôi đã từng thúc đẩy cho sự thuận tiện ở đây, nhưng bạn có thể cuộn của bạn sở hữu nếu tăng không phải là một tùy chọn hoặc sử dụng C++ 11 thay thế.

is_pod không phải là khá những gì bạn muốn thực sự, bạn có thể muốn có một đặc điểm is_container, nhưng that's not so trivial, you'll need to make a trait of your ownis_pod làm cho một xấp xỉ tốt về cách sử dụng những đặc điểm để chọn lọc cho phép hoạt động như trả lời đơn giản.

8

Sử dụng một đặc điểm:

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

Bạn có thể tìm thấy một đặc điểm phù hợp trong pretty printer code.

0

Khi người tiền nhiệm của tôi viết, bạn phải sử dụng một số loại đặc điểm. Có lẽ bạn nên sử dụng Boost cho rằng, nhưng nếu bạn không muốn để bạn chỉ có thể sử dụng một cái gì đó như thế này (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); 
} 

Nó không phải là một nhiệm vụ dễ dàng để phân biệt container từ mảng đơn giản, vì vậy tôi sử dụng ở đây kiểm tra xem loại có const_iterator hay không. Có lẽ bạn cũng nên kiểm tra xem nó có begin(), end() và những thứ khác bạn sẽ sử dụng trong mã sau này không.

1

Bạn có thể thử kỹ thuật Lỗi thay thế không phải là lỗi (SFINAE) tại đây.

Trước hết, bạn cần một chức năng để xác định xem một loại có thành viên iterator ...

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

Trong đoạn mã trên, cáC++ Chuẩn C đòi hỏi test(typename U::const_iterator*) được sử dụng trong ưu tiên cho mơ hồ" .. "so khớp thông số, miễn là U thực sự là cấu trúc/lớp với loại thành viên const_iterator. Nếu không, bạn có một "sự thất bại thay thế" - đó không phải là lỗi nghiêm trọng khi ngừng biên dịch (do đó SFINAE), và cố gắng tìm một hàm phù hợp được satified bởi test(...). Vì các kiểu trả về của hai hàm này khác nhau, toán tử sizeof có thể kiểm tra xem một hàm nào đã được đối sánh, thiết lập một boolean result một cách thích hợp.

Sau đó, bạn có thể chuyển tiếp yêu cầu để in điều cần mẫu chuyên ngành hỗ trợ họ ...

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: *" toán tử 'sizeof' có thể kiểm tra cái nào được gọi là" * - 'test()' là không bao giờ thực sự được gọi là cả. Bạn biết điều này rồi, tôi chắc chắn. – cdhowie

+0

@cdhowie: điểm công bằng - câu trả lời được cập nhật với "trùng khớp". Chúc mừng. –

Các vấn đề liên quan