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
?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With