// infix_iterator.h
//
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>
template <class T,
class charT=char,
class traits=std::char_traits<charT> >
class infix_ostream_iterator :
public std::iterator<std::output_iterator_tag,void,void,void,void>
{
std::basic_ostream<charT,traits> *os;
charT const* delimiter;
bool first_elem;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;
infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{}
infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s),delimiter(d), first_elem(true)
{}
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
{
// Here's the only real change from ostream_iterator:
// We don't print the delimiter the first time. After that,
// each invocation prints the delimiter *before* the item, not
// after. As a result, we only get delimiters *between* items,
// not after every one.
if (!first_elem && delimiter != 0)
*os << delimiter;
*os << item;
first_elem = false;
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator*() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++(int) {
return *this;
}
};
#endif
这是(至少打算)代替
std::ostream_iterator
的唯一替代品,唯一的区别是(至少在正常使用时)它是仅在项目之间而不是在每个项目之后打印定界符。使用它的代码如下所示:#include "infix_iterator.h"
std::vector<int> numbers = {1, 2, 3, 4};
std::copy(begin(numbers), end(numbers),
infix_ostream_iterator<int>(std::cout, ", "));
这样做的动机非常简单-有了
std::ostream_iterator
,您的列表就会像1, 2, 3, 4,
一样出现,但是有了infix_iterator,作为旁注,尽管我在此演示代码中使用了几个C ++ 11功能,但我相信迭代器本身对于C ++应该很好03-尽管如果有人看到对没有C ++ 11支持的编译器有问题的任何东西,我也想听听。编辑:如果有人关心,这里是新版本结合了@Konrad和@Loki的输入。谢谢大家。
// infix_iterator.h
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>
#include <string>
template <class T, class charT=char, class traits=std::char_traits<charT> >
class infix_ostream_iterator :
public std::iterator<std::output_iterator_tag, void, void, void, void>
{
std::basic_ostream<charT,traits> *os;
std::basic_string<charT> delimiter;
std::basic_string<charT> real_delim;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT, traits> ostream_type;
infix_ostream_iterator(ostream_type &s)
: os(&s)
{}
infix_ostream_iterator(ostream_type &s, charT const *d)
: os(&s),
real_delim(d)
{}
infix_ostream_iterator<T, charT, traits> &operator=(T const &item)
{
*os << delimiter << item;
delimiter = real_delim;
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator*() {
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator++() {
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator++(int) {
return *this;
}
};
#endif
#1 楼
没有什么专业(只是一些个人意见):一致的间距
infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{} // ^^^ No Space ^^^Trailing space
一致的类型命名
// Here we have & on the left
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
// Here we have it on the right
infix_ostream_iterator<T,charT,traits> &operator*() {
delimter
的拼写我在编写此代码时错了很多=>
delimiter
更容易阅读初始化程序列表
就像我更喜欢每行一个语句,我更喜欢在初始化程序列表中每行初始化一个变量(更易于阅读)。
它可能对性能没有影响,但我将删除
if
,因此代码如下所示:infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s)
, delimiter(d)
, first_elem(true)
{}
在构造中,
actualDelimter
指向用户提供的字符串(或空字符串),而delimter
设置为指向空字符串。评论
\ $ \ begingroup \ $
我一直在想这个。而不是直接存储用户的指针,并初始化定界符以指向空字符串,您是否看到我不对定界符和real_delim都使用std :: basic_string
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
2012年7月1日14:15
\ $ \ begingroup \ $
@JerryCoffin:我认为没有不利的一面。间接费用是在施工过程中发生的。即使大量复制,复制的额外成本也不会那么高。而使用operator <<完全不计所有这些
\ $ \ endgroup \ $
–马丁·约克
2012年7月1日在22:21
\ $ \ begingroup \ $
说副本比他以前的条件便宜是真的合理的假设吗?有什么方法可以量化吗?
\ $ \ endgroup \ $
–大卫
2012年7月3日于13:03
\ $ \ begingroup \ $
@戴夫。它并不便宜,而是更昂贵。但是您不会期望看到太多复制(就像我想说的那样)。因此,与通过这种方式实现的额外安全性相比,我认为额外的成本微不足道。
\ $ \ endgroup \ $
–马丁·约克
2012年7月3日15:26
\ $ \ begingroup \ $
我将delimiter和real_delim保留为charT const *,但在构造函数中的操作如下:infix_ostream_iterator(ostream_type&s,charT const * d = 0):os(&s),delimiter(“”),real_delimiter(d ?d:“”){}。这样一来,复制的成本就更少了。
\ $ \ endgroup \ $
– dalle
2014年1月22日7:30
#2 楼
我在代码中唯一可以批评的是infix运算符与指针/引用声明之间空格的不一致位置。空格。
class charT=char,
public std::iterator<std::output_iterator_tag,void,void,void,void>
…等,空格使用不一致。
: os(&s),delimiter(0), first_elem(true)
…等,不一致
&
的位置。我还将统一使用函数定义之间的空白行,并删除
template <…>
块与类标题之间以及类定义末尾的空白行。肯定是挑剔的,但这实际上是唯一值得批评的地方。
#3 楼
我还要声明第一个构造函数为显式的:地点。#4 楼
保持const char指针我同意Lokis建议的更改,除了我将
delimiter
和real_delim
保留为charT const *
并将构造函数更改为:infix_ostream_iterator(ostream_type &s, charT const *d = 0)
: os(&s)
, delimiter("")
, real_delim(d ? d : "")
{
}
那样,我们减少了复制字符串,而是只复制一个指针。
推导的类型
模仿在C ++ 14中对std :: less所做的更改,您可以将
operator=
设为模板,这样我们就无需指定这种类型的类。但这会破坏与std::ostream_iterator
接口的兼容性。template <class T>
infix_ostream_iterator<charT, traits> &operator=(T const &item)
{
*os << delimiter << item;
delimiter = real_delim;
return *this;
}
评论
\ $ \ begingroup \ $
可以说d的类型作为模板参数可能更好。我可以设想在某些代码中将其作为字符串文字(const char *),而在其他代码中将其作为std :: string。
\ $ \ endgroup \ $
– Toby Speight
17 Mar 9 '17 at 9:10
#5 楼
首先,这堂课的好主意!我同意其他用户(Loki,Konrad,user15108,dalle)已经提出的更改。此外,我注意到
infix_ostream_iterator
不是默认可构造的。这适合用例,并遵循std::ostream_iterator
的设计。因此,您也可以将std::basic_ostream
存储为参考成员变量。也就是说,现在您可以删除指针间接寻址,这将简化剩余的代码。例如,构造函数变为class infix_ostream_iterator : ... {
...
std::basic_ostream<charT,traits>& os;
...
}
,您可以直接写入流中
infix_ostream_iterator(ostream_type &s, charT const *d)
: os(s)
, real_delim(d)
{}
实现细节(您的界面相同)。我认为编译器无论如何都会生成相同的指令集。
期待看到类似Boost的此类!祝您好运。
#6 楼
我知道我已经晚了几年,但是我想提出一个主意:为什么分隔符和非分隔符必须为同一类型?您的初始版本使用的是charT const*
,而您的新版本使用的是std::basic_string<charT>
...,但要复制两个string
只是为了处理没有定界符,这似乎很浪费。我建议您使用一个重载的make_*
函数,它可以创建您的迭代器或创建标准的迭代器:您的类型始终使用定界符。 评论
\ $ \ begingroup \ $
也许只是我还没醒,但是我想我不确定这将如何工作/使用。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
15年12月11日在16:25
\ $ \ begingroup \ $
如果要使用定界符:make_ostream_iterator
\ $ \ endgroup \ $
–巴里
2015年12月11日下午16:27
\ $ \ begingroup \ $
这里的想法是能够支持诸如copy(b,e,out)之类的东西,并使[b ... e]的元素像a,b,c之类的东西出现,并在它们之间使用项,但不是最后一项(例如std :: ostream_iterator会生成)。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
2015年12月11日在16:33
\ $ \ begingroup \ $
@JerryCoffin不,我知道。但是,您也没有任何限制地支持infix_ostream_iterator
\ $ \ endgroup \ $
–巴里
2015年12月11日在16:40
\ $ \ begingroup \ $
哦,我知道了-是的,当您没有定界符时,它与普通的ostream_iterartor之间没有真正的区别。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
2015年12月11日在16:49
评论
为什么要使用charT const * d而不是const std :: basic_string@Dave:要模仿std :: ostream_iterator的界面?
您是否已提交给Boost上游?我似乎在Boost中找不到infix_iterator和infix_ostream_iterator。
@sukhmel:否-可以说,我在其他方面有些捉襟见肘,可以说是裂开了。
@JerryCoffin:这是最有用的工具;我真的很讨厌那些落后的delim。如果在SO的答案中使用缩小版本的版本,是否可以链接到这里? :)