2010-04-12 50 views
18

STL的常用定义输出迭代器,像这样:输出迭代器的VALUE_TYPE

template<class Cont> 
class insert_iterator 
: public iterator<output_iterator_tag,void,void,void,void> { 
    // ... 

为什么输出迭代定义value_typevoid算法知道它应该输出什么类型的值是很有用的。

例如,将URL查询"key1=value1&key2=value2&key3=value3"转换为包含键值字符串元素的任何容器的函数。

template<typename Ch,typename Tr,typename Out> 
void parse(const std::basic_string<Ch,Tr>& str, Out result) 
{ 
    std::basic_string<Ch,Tr> key, value; 
    // loop over str, parse into p ... 
     *result = typename iterator_traits<Out>::value_type(key, value); 
} 

SGI reference page of value_type提示这是因为无法取消引用输出迭代器。但这不是value_type的唯一用途:我可能想要实例化一个以将其分配给迭代器。

有什么替代方法可以用输出迭代器构造输出值?我考虑过的两种方法:

  • 接受函数参数,该函数参数将返回正确类型的对象。我仍然想要一个没有使用该函数对象参数的算法版本。
  • 要求输出容器容纳pair<string,string>,否则就是可以从中转换的类型。我想知道如果没有这个要求我可以做什么,也许允许任何可以从两个std::string构建的元素。
+0

我试图做的是概括的算法,使得它适用于任何类型的容器,可以从两个'string'小号构建,而不仅仅是'地图<字符串,字符串>'。然后,模板可以实例化为'* result = pair (key,value)'以及'* result = unicorn_pony(key,value)''。 – wilhelmtell 2010-04-12 02:47:07

+0

如果我可以添加一个引用:关于输出迭代器的论文[pdf],也会抱怨这个问题。 http://semantics.org/publications/02_02_multiout.pdf – wilhelmtell 2010-04-12 05:16:34

+1

该论文给出的例子正是为什么输出迭代器不需要定义值类型。他任意决定'MultiOut :: value_type'应该是'double',但实际上*可以隐式转换为'double'和'int'的任何*类型都是用于赋值运算符的合理值。 – 2010-04-12 07:01:41

回答

7

迭代器的实际值类型很可能是迭代器本身。 operator*可能很容易返回对*this的引用,因为实际工作是由赋值运算符完成的。您可能会发现*it = x;it = x;与输出迭代器具有完全相同的效果(我认为可能会采取特殊措施来防止后者编译)。

因此,定义真实值类型将是无用的。其定义为void,在另一方面,可以防止类似的错误:

typename Iter::value_type v = *it; //useless with an output iterator if it compiled 

我想这仅仅是输出迭代器概念的限制:它们是对象,其“滥用”操作符重载,以出现指针式,而实际上完全不同的事情正在发生。

虽然你的问题很有趣。如果你想支持任何容器,那么有问题的输出迭代很可能会std::insert_iteratorstd::front_insert_iteratorstd::back_insert_iterator。在这种情况下,您可以执行以下操作:

#include <iterator> 
#include <vector> 
#include <string> 
#include <map> 
#include <iostream> 

//Iterator has value_type, use it 
template <class T, class IterValue> 
struct value_type 
{ 
    typedef IterValue type; 
}; 

//output iterator, use the container's value_type 
template <class Container> 
struct value_type<Container, void> 
{ 
    typedef typename Container::value_type type; 
}; 

template <class T, class Out> 
void parse_aux(Out out) 
{ 
    *out = typename value_type<T, typename Out::value_type>::type("a", "b"); 
} 

template <template <class> class Out, class T> 
void parse(Out<T> out) 
{ 
    parse_aux<T>(out); 
} 

//variadic template in C++0x could take care of this and other overloads that might be needed 
template <template <class, class> class Out, class T, class U> 
void parse(Out<T, U> out) 
{ 
    parse_aux<T>(out); 
} 

int main() 
{ 
    std::vector<std::pair<std::string, std::string> > vec; 
    parse(std::back_inserter(vec)); 
    std::cout << vec[0].first << ' ' << vec[0].second << '\n'; 

    std::map<std::string, std::string> map; 
    parse(std::inserter(map, map.end())); 
    std::cout << map["a"] << '\n'; 

    //just might also support normal iterators 
    std::vector<std::pair<std::string, std::string> > vec2(1); 
    parse(vec2.begin()); 
    std::cout << vec2[0].first << ' ' << vec2[0].second << '\n'; 
} 

它仍然只会让您感觉如此。我想,人们可以借此进一步,所以它也可以管理,比方说,一个std::ostream_iterator<printable_type>,但在某些时候它会变得这么复杂,它需要一个神破译错误消息,应该什么出差错。

2

迭代器的value_type的用途是定义迭代器被解除引用时返回的类型。对于输出迭代器,解除引用运算符的唯一合法用途是将其与赋值运算符一起使用 - 形式为*output_iterator = value。取消引用输出迭代器时返回的类型与通过输出迭代器可以存储的类型不必有任何直接关系。唯一需要的关系是有一些将后一种类型分配给前一种类型的方法。

此外,输出迭代器可以存储多个类型的值,并且这些类型之间不必有任何关系。以Discarding the output of a function that needs an output iterator中描述的null_output_iterator为例。该迭代器可以接受存储任何类型的值。

+0

当你说_“迭代器的VALUE_TYPE的目的是定义时迭代器提领时,返回的类型” _,你可以请指定的参考?标准中的措词,也许呢?在该标准的第24.3.1节中,我看不到有关'value_type'是什么的细节,只有那些输出迭代器可以将其设置为void。在TCPL3e的19.2.2节中,Stroustrup只说“'value_type'是元素的类型”。 – wilhelmtell 2010-04-12 05:12:57

+0

从标准的部分24.1,“所有迭代我支持表达* 1,从而导致一些类,枚举的一个值,或内置型T,称为该值类型的迭代器的”。 根据第24.3.1节的规定,“[...]如果迭代器是迭代器的类型,则需要将iterator_traits :: value_type定义为迭代器的值类型[ ...]“。 另外,从部分24.3.1, “在一个输出迭代的情况下,[...] iterator_traits :: VALUE_TYPE [...被...]被定义为无效。” – 2010-04-12 05:44:28

+1

好吧,我知道输出迭代器的'value_type'设置为'void'。 '* i'是'i'的'value_type',程序员不允许解除引用'i',所以''i''仍然是'void'的一个飞跃。它不一定是“空白”。 'value_type'还有其他有效用途。我没有辩论这一点,我只是不明白从“你无法取消引用输出迭代器”到“让我们将输出迭代器的'value_type'定义为'void'”。 – wilhelmtell 2010-04-12 07:22:14