2013-01-23 135 views
11

我有點假設基於for循環將支持C風格的字符串範圍終止字符串

void print_C_str(const char* str) 
{ 
    for(char c : str) 
    { 
     cout << c; 
    } 
} 

該範圍然而,這並非如此,標準[stmt.ranged] (6.5.4)說,基於範圍換在3種可能性之一的工作原理:

  1. 範圍是一個數組
  2. 範圍是與可呼叫beginend方法的類
  3. 有ADL可達在相關命名空間(加上std命名空間)

當我在全局命名空間中添加對const char*beginend功能,我仍然得到錯誤(來自VS12和GCC 4.7)。

有沒有辦法讓基於範圍的for循環使用C風格的字符串?

我嘗試添加的過載namespace std這個工作,但我的理解是非法的重載添加到namespace std(這是正確的?)

+2

可以合法地專注於std名字空間的模板。 – inf

+6

@bamboon true,但是IIRC只適用於用戶定義的類型,這是一個重載而不是專門化,並且對於內置類型而不是UDT。 – Motti

+1

你爲什麼繞過C字符串? –

回答

19

如果你爲空字符結尾的字符串編寫了一個簡單的迭代器,你可以通過調用返回特殊範圍的指針的函數來完成,而不是將指針本身視爲範圍。

template <typename Char> 
struct null_terminated_range_iterator { 
public: 
    // make an end iterator 
    null_terminated_range_iterator() : ptr(nullptr) {} 
    // make a non-end iterator (well, unless you pass nullptr ;) 
    null_terminated_range_iterator(Char* ptr) : ptr(ptr) {} 

    // blah blah trivial iterator stuff that delegates to the ptr 

    bool operator==(null_terminated_range_iterator const& that) const { 
     // iterators are equal if they point to the same location 
     return ptr == that.ptr 
      // or if they are both end iterators 
      || is_end() && that.is_end(); 
    } 

private: 
    bool is_end() { 
     // end iterators can be created by the default ctor 
     return !ptr 
      // or by advancing until a null character 
      || !*ptr; 
    } 

    Char* ptr; 
} 

template <typename Char> 
using null_terminated_range = boost::iterator_range<null_terminated_range_iterator<Char>>; 
// ... or any other class that aggregates two iterators 
// to provide them as begin() and end() 

// turn a pointer into a null-terminated range 
template <typename Char> 
null_terminated_range<Char> null_terminated_string(Char* str) { 
    return null_terminated_range<Char>(str, {}); 
} 

和使用情況是這樣的:

for(char c : null_terminated_string(str)) 
{ 
    cout << c; 
} 

我不認爲這將失去任何表現。其實,我認爲這個更清晰。

+4

+1中,它*更清晰,因爲它正確地與'char *'和'char []'一起使用。'char []'的問題在於它本能地工作,但是做錯了事(它被視爲任何C數組,而不是以零終止的字符串,因此其中一個元素太長了) –

+0

+1出於好奇,是否有一個原因,這個解決方案會優先於'for(char c:std :: string(str))'?這對我來說似乎是一個明顯的解決方案。由於沒有人發佈它作爲答案,我只能猜測存在在這個解決方案中,你發佈了這個解決方案,或者使用'std :: string'來構建額外的需求來執行迭代。 – hmjd

+2

@hmjd是的,for(char c:std :: string(str))是一個與std :: string相比,這種方法的優點是這個抽象的運行時代價非常低:構造的額外對象非常便宜,與std :: string不同,它可能涉及動態分配,並會將整個字符串複製到它自己的緩衝區基本上,雖然我不會嘲笑看到std :: string用於此(除非分析證明它是性能問題),這提供了所需的功能,迭代,而std :: string提供不需要的功能,這是什麼成本。 –

2

A C-string是不是一個數組,它不是一個類有begin/end成員,並且ADL不會找到任何內容,因爲該參數是一個原語。可以說,這應該是普通的不合格查找,用ADL,其中在全局命名空間中找到一個函數。但是,考慮到措辭,我認爲這是不可能的。

2

可能的解決方法是將空值終止的字符串換行爲另一種類型。最簡單的實現如下(它的性能低於R. Martinho Fernandes的建議,因爲它叫strlen,但它的代碼也少得多)。

class null_terminated_range { 
    const char* p: 
public: 
    null_terminated_range(const char* p) : p(p) {} 
    const char * begin() const { return p; } 
    const char * end() const { return p + strlen(p); } 
}; 

用法:

for(char c : null_terminated_range(str))