有时,我需要使用通用的C ++迭代器范围接口[first, last)
将C字符串传递给函数。对于这些情况,是否有标准的C ++迭代器类,或者不需要复制字符串或调用strlen()
的标准方法呢?
编辑:
我知道我可以将指针用作迭代器,但是我必须知道字符串的结尾,什么使我需要调用strlen()
。
EDIT2: 虽然我不知道这种迭代器是否标准化,但我当然知道这是可能的。回应讽刺的答案和评论,这是存根(不完整,未经测试):
class CStringIterator
{
public:
CStringIterator(char *str=nullptr):
ptr(str)
{}
bool operator==(const CStringIterator& other) const
{
if(other.ptr) {
return ptr == other.ptr;
} else {
return !*ptr;
}
}
/* ... operator++ and other iterator stuff */
private:
char *ptr;
};
EDIT3: 具体来说,我对forward iterator感兴趣,因为当我知道算法只需要执行一次时,我想避免对sring进行两次迭代。
答案 0 :(得分:4)
没有任何显式的迭代器 class ,但是常规的原始指针也是有效的迭代器。但是,C字符串的问题在于它们没有附带本机末端迭代器,这使得它们无法在基于范围的for循环中使用–至少直接...
不过,您可能想尝试以下模板:
template <typename T>
class Range
{
T* b;
public:
class Sentinel
{
friend class Range;
Sentinel() { }
friend bool operator!=(T* t, Sentinel) { return *t; }
public:
Sentinel(Sentinel const& o) { }
};
Range(T* begin)
: b(begin)
{ }
T* begin() { return b; }
Sentinel end() { return Sentinel(); }
};
用法:
for(auto c : Range<char const>("hello world"))
{
std::cout << c << std::endl;
}
它最初旨在迭代main的以null终止的argv,但是可以使用 any 指向以null终止的数组的指针-C字符串也是如此...
秘密正在与前哨进行比较,后者实际上进行了完全不同的比较(当前指针指向终止空值(指针))...
编辑:C ++ 17之前的版本:
template <typename T>
class Range
{
T* b;
public:
class Wrapper
{
friend class Range;
T* t;
Wrapper(T* t) : t(t) { }
public:
Wrapper(Wrapper const& o) : t(o.t) { }
Wrapper operator++() { ++t; return *this; }
bool operator!=(Wrapper const& o) const { return *t; }
T operator*() { return *t; }
};
Range(T* begin)
: b(begin)
{ }
Wrapper begin() { return Wrapper(b); }
Wrapper end() { return Wrapper(nullptr); }
};
答案 1 :(得分:4)
实际上,是的。在c ++ 17中。
C ++ 17引入了std::string_view
,它可以由c样式的字符串构造。
std::string_view
是一个随机访问(代理)容器,它当然完全支持迭代器。
请注意,虽然理论上从const char*
构造string_view会调用std::strlen
,但是当编译器在编译时知道字符串的长度时,它可以(并且gcc当然可以)取消调用
示例:
#include <string_view>
#include <iostream>
template<class Pointer>
struct pointer_span
{
using iterator = Pointer;
pointer_span(iterator first, std::size_t size)
: begin_(first)
, end_(first + size)
{
}
iterator begin() const { return begin_; }
iterator end() const { return end_; }
iterator begin_, end_;
};
int main(int argc, char** argv)
{
for(auto&& ztr : pointer_span(argv, argc))
{
const char* sep = "";
for (auto ch : std::string_view(ztr))
{
std::cout << sep << ch;
sep = " ";
}
std::cout << std::endl;
}
}
请参见示例输出here
答案 2 :(得分:3)
是否有用于C字符串的标准C ++迭代器?
是的。指针是数组的迭代器。 C字符串是char
的(空终止)数组。因此char*
是C字符串的迭代器。
...使用通用的C ++迭代器范围接口
[first, last)
就像所有其他迭代器一样,要具有范围,您需要具有结束迭代器。
如果您知道或可以假设一个数组完全包含字符串,仅此而已,则可以使用std::begin(arr)
(std::begin
恒定时间获得迭代器范围(对于衰减到C的C数组是多余的)指针,但对于对称性很好)和std::end(arr) - 1
。否则,您可以对数组中的偏移量使用指针算法。
必须谨慎考虑空终止符。必须记住,数组的整个范围都包含字符串的空终止符。如果希望迭代器范围表示不带终止符的字符串,请从数组的结束迭代器中减去一个,这在上一段中作了说明。
如果没有数组,而只有一个指针-begin迭代器-您可以通过将字符串的开始位置提前以获取end迭代器。由于指针是随机访问迭代器,因此这种进步是一项恒定的操作。如果您不知道长度,可以调用std::strlen
来找出长度(这不是恒定的操作)。
例如,std::sort
接受一定范围的迭代器。您可以像这样对C字符串进行排序:
char str[] = "Hello World!";
std::sort(std::begin(str), std::end(str) - 1);
for(char c : "test"); // range-for-loops work as well, but this includes NUL
如果您不知道字符串的长度:
char *str = get_me_some_string();
std::sort(str, str + std::strlen(str));
具体地说,我对forward iterator
感兴趣
指针是一个随机访问迭代器。所有随机访问迭代器也是前向迭代器。指针符合链接的迭代器概念中列出的所有要求。
答案 3 :(得分:2)
可以编写这样的迭代器,这样的方法应该起作用:
struct csforward_iterator :
std::iterator<std::bidirectional_iterator_tag, const char, void> {
csforward_iterator( pointer ptr = nullptr ) : p( ptr ) {}
csforward_iterator& operator++() { ++p; return *this; }
csforward_iterator operator++(int) { auto t = *this; ++p; return t; }
csforward_iterator& operator--() { --p; return *this; }
csforward_iterator operator--(int) { auto t = *this; --p; return t; }
bool operator==( csforward_iterator o ) {
return p == o.p or ( p ? not ( o.p or *p ) : not *o.p );
}
bool operator!=( csforward_iterator o ) { return not operator==( o ); }
void swap( csforward_iterator &o ) { std::swap( p, o.p ); }
reference operator*() const { return *p; }
pointer operator->() const { return p; }
private:
pointer p;
};
不幸的是,没有提供标准的标准,它可能是char
类型的模板(例如std::string
)。
答案 4 :(得分:1)
恐怕不是,最后,您将需要一个指向要调用strlen
的字符串末尾的指针。
答案 5 :(得分:1)
如果您有字符串文字,则无需使用std::strlen
就可以得到结束迭代器。如果只有char*
,则必须编写自己的迭代器类或依靠std::strlen
来获取最终迭代器。
字符串文字的说明代码:
#include <iostream>
#include <utility>
template <typename T, size_t N>
std::pair<T*, T*> array_iterators(T (&a)[N]) { return std::make_pair(&a[0], &a[0]+N); }
int main()
{
auto iterators = array_iterators("This is a string.");
// The second of the iterators points one character past the terminating
// null character. To iterate over the characters of the string, we need to
// stop at the terminating null character.
for ( auto it = iterators.first; it != iterators.second-1; ++it )
{
std::cout << *it << std::endl;
}
}
答案 6 :(得分:1)
为了获得最终的安全性和灵活性,您需要包装迭代器,并且迭代器必须带有某种状态。
问题包括:
请注意,如果在容器范围之外随机寻找它,则不是“迭代器的问题”,它可以合法地经过string_view.end()。同样相当标准的是,这样一个损坏的迭代器不能再递增到end()。
这些情况中最令人痛苦的是,可以将end减一,减一和取消引用(通常不能,但是对于字符串来说,它是一个空字符)。这意味着结束对象需要一个标志,它是结束标志和起始地址,以便在发生以下任何一种操作时,都可以使用strlen()找到实际的结束标志。
答案 7 :(得分:0)
在这些情况下是否有标准的C ++迭代器类,或无需复制字符串就可以实现的标准方法
迭代器是指针的概括。具体来说,它们的设计使指针是有效的迭代器。
注意pointer specializations of std::iterator_traits
。
我知道我可以将指针用作迭代器,但是我必须知道字符串的结尾
除非您有其他方法可以知道字符串的结尾,否则调用strlen
是您的最佳选择。如果有魔术迭代器包装器,则还必须调用strlen
。
答案 8 :(得分:0)
对不起,迭代器是通常从可迭代实例获得的东西。由于var jagged3D= new int[][][]
{
new int[][]{
new int[]{6},
new int[]{0},
new int[]{13}
}
};
是基本类型,而不是类。您如何看待诸如var jaggedCustom = new Custom[][] {
new Custom[]{
new Custom{
A =3,
B =0,
C = new int[]{13}
}
}
};
或char *
之类的事情。
顺便说一句,如果您需要迭代.begin()
并知道它已被nul终止,那么请进行循环。您只需执行以下操作即可。
.end()
但是事实是您不能使用C ++中定义的迭代器,因为char *p
是基本类型,没有构造函数,没有关联的析构函数或方法。