2010-04-12 47 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