2013-01-23 134 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))