6

The std::back_insert_iterator has value_type equal to void, but it also has a protected member container that holds a pointer to the underlying Container. I am trying to write a traits class to extract the container's value_type, along these lines:

#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, "");
}

Live Example

However, this (more or less expectedly) runs into incomplete type errors. Is there anyway around this in order to get extract the container's value_type?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Why don't you use a nested type that derives from the iterator? [Live example](http://coliru.stacked-crooked.com/a/3547c063ddfb1ddb) – dyp Mar 15 '15 at 20:23
  • @dyp I was being daft, `back_insert_iterator` has a nested typedef `container_type` even – TemplateRex Mar 15 '15 at 20:27
  • See recently added live example. -- ooooh that is a public member. I see – dyp Mar 15 '15 at 20:27
  • @dyp your live example is nice, but it also uses the nested typedef. When doing this with `declval` and the `container` member, you can e.g. make the parent class a friend of the helper class (that derives from the template parameter) and then get access to its protected member. – TemplateRex Mar 15 '15 at 20:31

2 Answers2

9

The answer by @Rapptz is correct but for generic code (i.e. when it is not clear a priori whether one deals with a raw T* or a back_insert_iterator or one of the Standard Library's other output iterators), a more systematic approach is necessary.

To that effect, below a definition of a class template output_iterator_traits in a user-defined 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, class traits>
struct output_iterator_traits<std::ostream_iterator<T, charT, traits>>
:
        std::iterator<std::output_iterator_tag, T>
{};

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

} // namespace xstd

The unspecialized version simply inherits from std::iterator_traits<T>, but for the 6 output iterators defined in the <iterator> and <memory> headers, the specializations inherit from std::iterator<std::output_iterator_tag, V> where V is the type appearing as an argument of the iterator's operator=(const V&).

For the insert iterators, this corresponds to typename Container::value_type, for raw storage iterators to T, and for ostream and ostreambuf iterators to T and charT, respectively.

A generic algorithm of the form

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 */ };
     }
}

will then transparantly work with both raw pointers and the Standard Library's output iterators.

neonxc
  • 802
  • 9
  • 21
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • A very clean and STL-like solution, but what did you compile it with? I believe that default template arguments are not allowed in partial specializations. – neonxc Nov 26 '19 at 06:44
2

You could just use container_type that it has:

#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, "");
}

Live Example

Rapptz
  • 20,807
  • 5
  • 72
  • 86