C++类字符串类型的迭代器

C++ iterator to string-like types

本文关键字:迭代器 类型 字符串 C++      更新时间:2023-10-16

计划是实现一个具有两个构造函数的类。第一个 ctor 应该采用类似字符串的类型,第二个 ctor 应该采用两个迭代器(开始和结束(到字符串类对象的容器。在内部,该类将使用const char*、以 null 结尾的 C 字符串。对于第一个 ctor,这是一项简单的任务,我可以明确表示。传入具有str.c_str()或类似字符串类型的C++string可以正常工作。但是,对于第二个 ctor,我不太确定应该如何实现它。通常,它应该是具有可转换为const char*的值类型的LegcayInputIterator。从我的头顶上,我可以想到至少 4 种我想要支持的类型,而让第二个 ctor 超载这么多似乎是错误的方法。

为什么我的第二个 ctor 编译失败?应定义++!=*运算符。将两个迭代器传递给T但实际上允许我可以从中获取const char*的不同字符串类类型(请参阅代码中的示例 5(的工作方法是什么?

示例代码应该说明我的方法,但不幸的是不起作用。

#include <vector>
#include <array>
#include <cstdio>
#include <string>
#include <iterator>
struct Example {
using CtorIterator = std::iterator<std::input_iterator_tag, const char*>;
Example(const char *text)
{
std::printf("Called single argument ctor, text = %sn", text);
}
Example(CtorIterator begin, CtorIterator end)
{
std::printf("Called iterator ctor, text = [");
for (auto it = begin; it != end; ++it)
std::printf("%s, ", *it);
std::printf("]n");
}
};
int main(int argc, char **argv) {
Example ex1("Hello");
std::string arg2("String");
Example ex2(arg2.c_str());
std::vector<const char*> args3{"A", "B", "C"};
std::array<const char*, 3> args4{"D", "E", "F"};
std::vector<std::string> args5{"G", "H", "I"};
Example ex3(args3.begin(), args3.end());
Example ex4(args4.begin(), args4.end());
Example ex5(args5.begin(), args5.end()); // won't work
return 0;
}
std::iterator

不应该这样使用。它被引入到库中,通过提供某些成员类型(如iterator_categoryvalue_type(来支持用户定义的迭代器。std::iterator是一个空类,按价值获取它是没有意义的。通过引用获取它也没有多大意义,因为迭代器不必从中派生。std::iterator自 C++17 日起已弃用。

LegcayInputIterator不是一个类,而是一个类型应该满足的要求的集合。类型本身不是固定的,应该是模板参数。从 C++20 开始,当概念成为语言功能时,您将能够编写如下内容:

template<typename InputIt> requires InputIterator<InputIt>
Example(It begin, It end) { ... }

template<InputIterator InputIt>
Example(It begin, It end) { ... }

检查InputIt是否确实满足输入迭代器的要求。这里InputIterator是一个概念,有关特定示例,请参阅此处。在那之前,您只需写:

template<typename InputIt>
Example(InputIt begin, InputIt end) { ... }

例如,std::vector的构造函数来自范围[first, last)是这样声明的:

template<class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());

惯用的方法是改用模板

template<typename IterT>
Example(IterT begin, IterT end) { ... }

你使用std::iterator<...>的方式不起作用,因为很少有容器迭代器实际上是std::iterator<...>类型,并且没有转换运算符。

它并没有真正解决为什么它不起作用的问题,但你可以利用std::beginstd::end

struct Example {
Example(const char *text)
{
std::printf("Called single argument ctor, text = %sn", text);
}
template<typename T>
Example(const T &t)
{
std::printf("Called iterator ctor, text = [");
for (auto it = std::begin(t); it != std::end(t); ++it)
std::printf("%s, ", *it);
std::printf("]n");
}
};

用于完全在范围底座上的继电器,用于:

struct Example {  
Example(const char *text)
{
std::printf("Called single argument ctor, text = %sn", text);
}
template<typename T>
Example(const T &t)
{
std::printf("Called iterator ctor, text = [");
for (const auto &c : t)
std::printf("%s, ", c);
std::printf("]n");
}
};