问题
Let's say that I have a C container (e.g., MyContainer
) with contained objects stored as void*
pointers. The only way to iterate through the elements of this container is via two interface functions:
getFirstElem(MyContainer const&, void*)
: Outputs the first element of the container.getNextElem(MyContainer const&, void*)
: Outputs the next element of the container.
I want to code a generic function that iterates through the elements of this C container via the interface functions mentioned above and copy their values into a C++ container (e.g. std::vector
).
What I've done so far:
template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
typename std::iterator_traits<OutputIterator>::value_type elem;
if(getFirstElem(cont, &elem)) {
do {
*first = elem;
++first;
} while(getNextElem(cont, &elem))
}
}
The above example works OK with normal iterators. However, it fails to compile with output iterators (e.g., copy_container(cont, std::back_inserter(myvector));
).
The reason is that std::iterator_traits::value_type
results in void
in cases where the argument type is an output iterator.
Is there a way to make this generic function work for output iterators as well?
I know that in C++11 it could be done by using decltype
(e.g., decltype(*first)
), but I'm particularly interested in pre-C++11 solutions since I use an old C++ compiler (gcc v4.4.7).
回答1:
As correctly observed, the value_type
of an output iterator is void
. So there not much to do apart from replacing this :
typename std::iterator_traits<OutputIterator>::value_type elem;
with this
decltype(*first) elem;
(even though the Standard doesn't guarantee it'll work - a proxy might be returned by dereferencing an output iterator).
As you said no C++11 solution so a redesign might be needed. Here are some options:
1. Pass the container
Instead of an iterator to the first element, you could pass a reference to the container. It seems like all you want is a push_back
.
template<template<typename,typename> class stlContainer>
void copy_container(
MyMontainer const &cont, OutputIterator first)
{
// insertion in stlContainer
then all you need is a layer of traits to dispatch to the right implementation of insertion per container
2. Pass an extra template parameter
The value type could be an extra template parameter.
template<typename value_type, typename OutputIterator>
void copy_container(MyMontainer const &cont, OutputIterator first)
{
value_type elem;
...
回答2:
You may use typetraits and specialization
template <typename IT>
struct it_value_type
{
typedef typename std::iterator_traits<IT>::value_type elem;
};
template <typename Container>
struct it_value_type<std::back_insert_iterator<Container>>
{
typedef typename Container::value_type elem;
};
template <typename Container>
struct it_value_type<std::front_insert_iterator<Container>>
{
typedef typename Container::value_type elem;
};
And then you code becomes:
template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
typename it_value_type<OutputIterator>::elem elem;
if (getFirstElem(cont, &elem)) {
do {
*first = elem;
++first;
} while (getNextElem(cont, &elem));
}
}
回答3:
There are different ways to solve this, I did it like this:
template <class T>
std::enable_if_t<!std::is_same_v<typename T::container_type::value_type, void>, size_t> read(T first, size_t count)
{
typedef typename T::container_type::value_type value_type;
I have another one for the usual iterator.
来源:https://stackoverflow.com/questions/25118328/how-to-get-the-value-type-from-an-output-iterator