Выходной итератор value_type - PullRequest
22 голосов
/ 12 апреля 2010

STL обычно определяет выходной итератор следующим образом:

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

Почему выходные итераторы определяют value_type как void? Было бы полезно, чтобы алгоритм знал, какой тип значения он должен выводить.

Например, функция, которая переводит 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 value_type намекает на это, поскольку невозможно разыменовать выходной итератор. Но это не единственное использование value_type: я мог бы захотеть создать его экземпляр, чтобы назначить его итератору.

Какой альтернативный подход существует для построения значения для вывода с помощью итератора вывода? Два подхода, которые я рассмотрел:

  • Примите параметр функтора, который будет возвращать объект правильного типа. Я все еще хочу иметь версию алгоритма, которая не принимает этот параметр объекта функции.
  • Требуется, чтобы выходной контейнер содержал pair<string,string>, или же тип, конвертируемый из этого Интересно, могу ли я обойтись без этого требования, возможно, разрешить любой элемент, который можно построить из двух std::string с.

Ответы [ 2 ]

10 голосов
/ 12 апреля 2010

Реальным типом значения итератора вполне может быть сам итератор. operator* может легко просто вернуть ссылку на *this, потому что реальная работа выполняется оператором присваивания. Вы можете обнаружить, что *it = x; и it = x; имеют одинаковый эффект с итераторами вывода (я полагаю, что могут быть предприняты специальные меры для предотвращения компиляции последних).

Таким образом, определение типа реального значения было бы столь же бесполезным. С другой стороны, определение его как void может предотвратить такие ошибки, как:

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

Я полагаю, что это только предел концепции выходных итераторов: они являются объектами, которые "злоупотребляют" перегрузкой операторов, так что кажутся указательными, тогда как в действительности происходит нечто совершенно иное. 1013 *

Твоя проблема интересная. Если вы хотите поддерживать какой-либо контейнер, то рассматриваемые выходные итераторы, вероятно, будут std::insert_iterator, std::front_insert_iterator и std::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 голосов
/ 12 апреля 2010

Целью value_type итератора является определение типа, который возвращается при разыменовании этого итератора. Для выходных итераторов единственным допустимым использованием оператора разыменования является его использование в сочетании с оператором присваивания в форме *output_iterator = value. Тип, возвращаемый при разыменовании выходного итератора, не обязательно имеет прямую связь с типами, которые могут быть сохранены через выходной итератор. Единственное необходимое соотношение - это то, что есть какой-то способ присвоения последних типов первому типу.

Кроме того, выходной итератор может хранить значения нескольких типов, и эти типы не должны иметь никаких отношений друг с другом. Возьмем, к примеру, null_output_iterator, описанный в Отбрасывание вывода функции, которой требуется выходной итератор . Этот итератор может принимать для хранения любой тип значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...