Класс Traits для извлечения value_type контейнера из back_insert_iterator

std::back_insert_iterator имеет value_type равно void, но он также имеет protected член container который содержит указатель на базовый Container, Я пытаюсь написать класс черт, чтобы извлечь контейнер value_typeвдоль этих линий:

#include <iterator>
#include <type_traits>
#include <vector>

template<class OutputIt>
struct outit_vt
:
    OutputIt
{
    using self_type = outit_vt<OutputIt>;
    using value_type = typename std::remove_pointer_t<decltype(std::declval<self_type>().container)>::value_type;
};

int main()
{
    std::vector<int> v;
    auto it = std::back_inserter(v);
    static_assert(std::is_same<outit_vt<decltype(it)>::value_type, int>::value, "");
}

Живой пример

Однако это (более или менее ожидаемое) приводит к неполным ошибкам типа. Есть ли что-нибудь вокруг этого, чтобы извлечь контейнер value_type?

2 ответа

Решение

Ответ @Rapptz правильный, но для общего кода (то есть, когда априори не ясно, имеет ли дело T* или back_insert_iterator или один из других выходных итераторов Стандартной библиотеки), необходим более систематический подход.

Для этого ниже определения шаблона класса output_iterator_traits в пользовательском namespace xstd,

#include <iterator>             // iterator, iterator_traits, input_iterator_tag, output_iterator_tag, random_access_iterator_tag
                                // back_insert_iterator, front_insert_iterator, insert_iterator, ostream_iterator, ostreambuf_iterator
#include <memory>               // raw_storage_iterator

namespace xstd {

template<class T>
struct output_iterator_traits
:
        std::iterator_traits<T>
{};

template< class OutputIt, class T>
struct output_iterator_traits<std::raw_storage_iterator<OutputIt, T>>
:
        std::iterator<std::output_iterator_tag, T>
{};

template<class Container>
struct output_iterator_traits<std::back_insert_iterator<Container>>
:
        std::iterator<std::output_iterator_tag, typename Container::value_type>
{};

template<class Container>
struct output_iterator_traits<std::front_insert_iterator<Container>>
:
        std::iterator<std::output_iterator_tag, typename Container::value_type>
{};

template<class Container>
struct output_iterator_traits<std::insert_iterator<Container>>
:
        std::iterator<std::output_iterator_tag, typename Container::value_type>
{};

template <class T, class charT = char, class traits = std::char_traits<charT>>
struct output_iterator_traits<std::ostream_iterator<T, charT, traits>>
:
        std::iterator<std::output_iterator_tag, T>
{};

template <class charT, class traits = std::char_traits<charT>>
struct output_iterator_traits<std::ostreambuf_iterator<charT, traits>>
:
        std::iterator<std::output_iterator_tag, charT>
{};

} // namespace xstd

Неспециализированная версия просто наследует от std::iterator_traits<T>, но для 6 выходных итераторов, определенных в <iterator> а также <memory> заголовки, специализации наследуются от std::iterator<std::output_iterator_tag, V> где V тип, появляющийся в качестве аргумента итератора operator=(const V&),

Для итераторов вставки это соответствует typename Container::value_typeдля необработанных итераторов хранения T, и для ostream а также ostreambuf итераторы в T а также charTсоответственно.

Общий алгоритм вида

template<class InputIt, class OutputIt>
auto my_fancy_algorithm(InputIt first, InputIt last, OutputIt dest)
{
     using T = typename xstd::output_iterator_traits<OutputIt>::value_type;
     for (; first != last; ++first) {
         // ... construct arguments from *first
         *dest++ = T{ /* arguments */ };
     }
}

затем будет работать как с необработанными указателями, так и с итераторами вывода стандартной библиотеки.

Вы могли бы просто использовать container_type что оно имеет:

#include <iterator>
#include <type_traits>
#include <vector>

template<typename T>
struct outit_v {
    using container_type = typename T::container_type;
    using value_type = typename container_type::value_type;
};

int main()
{
    std::vector<int> v;
    auto it = std::back_inserter(v);
    static_assert(std::is_same<outit_v<decltype(it)>::value_type, int>::value, "");
}

Живой пример

Другие вопросы по тегам