2012-07-16 49 views
1

我想爲庫中的std :: string容器提供基於通用行的IO。 由於字符串可能包含空格,因此它是基於行的。 下面的代碼似乎工作正常,但我不確定這是否是最好的方式去或它是否造成一些含糊不清,我沒有把握。對於std :: string的非關聯容器io

#define boostForeach BOOST_FOREACH 

template< template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container > 
std::ostream& operator<< (std::ostream& o, Container<std::string>const & container){ 
    boostForeach(std::string const& str, container) { 
    o << str << "\n"; 
    } 
    return o; 
} 

template< template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container > 
std::istream& operator>> (std::istream& in, Container<std::string>& container){ 
    container.clear(); 
    std::string buf; 
    while(getline(in, buf)) { 
    if(buf.empty()) break; //stop if empty line found to separate map from other data 
    container.insert(container.end(),buf); 
    } 
    return in; 
} 

所以問題是:這是安全和健全的嗎?

+1

你可能想[看看這裏](http://codereview.stackexchange.com/)。 :) – netcoder 2012-07-16 21:27:08

+0

@netcoder:太棒了!不知道這一個,一定會試一試 – Martin 2012-07-16 21:31:20

回答

2

可以使用std::copy()輸出寫入算法:

std::copy(container.begin(), 
    container.end(), 
    std::ostream_iterator<std::string>(o, "\n")); 

您可以使用輸入迭代器段輸入,即,多行由空行分隔:

class istream_paragraph_iterator: public std::iterator<std::forward_iterator_tag,std::string>{ 
    std::istream* stream; 
    std::string line; 
public: 

    istream_paragraph_iterator() : stream(0) {} 

    istream_paragraph_iterator(std::istream& stream) : stream(&stream) {++*this; //get the first element 
    } 

    std::string operator*() const { 
     return line; 
    } 

    const std::string* operator->() const { 
     return &line; 
    } 

    istream_paragraph_iterator& operator++() { 
     if (stream && (!std::getline(*stream, line) || line.empty())) 
      stream = 0; 
     return *this; 
    } 

    istream_paragraph_iterator operator++(int) { 
     istream_paragraph_iterator previous(*this); 
     ++*this; 
     return previous; 
    } 

    bool operator==(const istream_paragraph_iterator& other) const { 
     return stream == other.stream; 
    } 

    bool operator!=(const istream_paragraph_iterator& other) const { 
     return !(*this == other); 
    } 

}; 

然後你可以使用編寫輸入算法,以及:

std::copy(istream_paragraph_iterator(in), 
    istream_paragraph_iterator(), 
    std::back_inserter(container)); 

邏輯分離成一個迭代器類型,可以使輸入和輸出的算法參數化,從而更普遍。在模板庫中,這通常是件好事。我會避免爲標準容器添加重載,因爲你不知道它們在每個平臺上都做了正確的事情;基於迭代器的算法更加便攜,並且不必編寫所有的文件。

2

第一個問題是標準容器中模板參數的數量不是標準強制的,這意味着您的代碼在任何有任何額外參數的平臺上都會失敗。

一般來說,我不會爲未定義的類型提供操作符重載,特別是對於標準容器。原因是你不能這樣做正確。尤其是你不能在容器所在的命名空間中聲明,這意味着ADL不會做出挑選正確過載的魔術。如果你想要一個幫助函數,你應該提供一個名爲函數的函數,如果需要幫助查找,它會更簡單。

試試這個:

#include <iostream> 
#include <vector> 
#include <string> 

template < template<typename,typename> class Container > 
std::ostream& operator<<(std::ostream& o, Container<std::string> const & c); 
namespace A { 
    struct S {}; 
    std::ostream& operator<<(std::ostream& o, S const &); 
    void f() { 
     std::vector<std::string> v; 
     std::cout << v; 
    } 
} 
int main() { 
    A::f(); 
} 
+0

謝謝你的反例。你說得很對,我完全忽略了不同命名空間的影響 – Martin 2012-07-17 07:46:09

+0

@Martin:我看到了你的編輯請求。因此我修改了代碼以適當地失敗。請注意,不需要模板或函數定義,並且模板參數的模板參數的名稱不需要事件然後(並且不那麼如此默認值) – 2012-07-17 14:54:02

+0

On Ideone您的代碼不帶結構S和根據運算符仍然失敗:prog.cpp:6:錯誤:模板參數的錯誤數量(1,應該是2) prog.cpp:5:錯誤:爲'模板<類,類>類容器'提供。 http://ideone.com/5uMxO std :: vector的一個模板參數有一個默認值。這就是爲什麼恕我直言,完整的規格可能是必要的。如果struct S不存在,我添加了模板定義以使代碼運行。 – Martin 2012-07-18 13:45:58