5

I have a function where I need an iterator's underlying data type as return type, like this:

#include <iostream>
#include <vector>
#include <iterator>

template<class T, class ForwardIterator>
std::vector<T> get_odd(ForwardIterator data_begin, ForwardIterator data_end)
{
    std::vector<T> ret;
    std::copy_if(data_begin, data_end, std::back_inserter(ret), [](int x) { return x % 2; });
    return ret;
}

int main()
{
    std::vector<int> vi { 1, 2, 3, 4, 5 };
    for (auto i : get_odd<int>(vi.begin(), vi.end()))
        std::cout << i << ", ";
    std::cout << std::endl;

    std::vector<unsigned int> vui{ 9UL, 8UL, 7UL, 6UL, 5UL };
    // Note the 'char' I provided to the function, this will print weird chars
    for (auto i : get_odd<char>(vui.begin(), vui.end()))
        std::cout << i << ", ";
    std::cout << std::endl;

    return 0;
}

In the main(), I have to explicitly provide data type 'int' or 'char' to the get_odd function, is there a way to find out iterator's underlying data type, and let template automatically deduct the correct data type?

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
dguan
  • 1,023
  • 1
  • 9
  • 21
  • 1
    FYI, `get_odd` does not require a forward iterator, an input iterator will do just fine. – Casey Aug 21 '14 at 17:13

4 Answers4

6

Iterator properties can be queried using std::iterator_traits:

template<class ForwardIterator>
std::vector<typename std::iterator_traits<ForwardIterator>::value_type> 
get_odd(ForwardIterator data_begin, ForwardIterator data_end)
{
    std::vector<typename std::iterator_traits<ForwardIterator>::value_type> ret;
    std::copy_if(data_begin, data_end, std::back_inserter(ret), [](int x) { return x % 2; });
    return ret;
}
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
2

If your compiler supports C++11, you can use:

#include <type_traits>
// ...
typename std::remove_reference<decltype(*data_begin)>::type

And probably typedef for convenience:

typedef typename std::remove_reference<decltype(*data_begin)>::type ContainedType;
std::vector<ContainedType> ret;
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
dlf
  • 9,045
  • 4
  • 32
  • 58
  • but how can I put std::remove_reference::type into the funtion's return type? – dguan Aug 21 '14 at 15:20
  • 3
    @dguan Use a trailing return type – Praetorian Aug 21 '14 at 15:21
  • @Angew My compiler rejects the code the way you edited it... ("typename cannot be used outside a template declaration") – dlf Aug 21 '14 at 15:23
  • 1
    @dguan See [this](http://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax) for info on trailing return types. The downside is that you wouldn't be able to use the `typedef` there, so you'd have to repeat the entire remove_reference etc. expression. – dlf Aug 21 '14 at 15:27
  • 1
    @dlf the `template` is supposed to be started in the `...` portion – Yakk - Adam Nevraumont Aug 21 '14 at 15:31
  • @dlf Then obviously you're using it someplace outside a tempalte. Unlike the OP, who works inside a function template. – Angew is no longer proud of SO Aug 21 '14 at 15:31
  • @Angew. Got it (it seems odd that the requirement is based on whether the line happens to fall within a template or not, but after re-checking the purpose of the keyword, it makes sense). VC++ is really forgiving (too forgiving) about the use of `typename`, so I have never been entirely clear on exactly what the rules are. – dlf Aug 21 '14 at 15:35
  • 1
    @dlf There's a [detailed canonical question about `typename` and `template`](http://stackoverflow.com/q/610245/1782465) here on SO. – Angew is no longer proud of SO Aug 21 '14 at 15:40
  • @dlf If what follows is a dependent type (a type that depends on a template parameter), you need to disambiguate it. It could be a value or a type, if you did not clear the dependency, and the C++ langauge does not require that dependency to be cleared until the template is instantiated, but it does require you to know if it is a type or a value before that point. – Yakk - Adam Nevraumont Aug 21 '14 at 15:40
  • @40two Neither will the `copy_if`, which requires at least input iterators. Also note that the OP's function suggests they are at least forward iterators. – Angew is no longer proud of SO Aug 21 '14 at 16:01
2

This comes up often enough that I use a convenient template alias to wrap iterator_traits:

template <typename I>
using ValueType = typename std::iterator_traits<I>::value_type;

template<class ForwardIterator>
std::vector<ValueType<ForwardIterator>>
get_odd(ForwardIterator data_begin, ForwardIterator data_end)
{
    std::vector<ValueType<ForwardIterator>> ret;
    std::copy_if(data_begin, data_end, std::back_inserter(ret), [](int x) {
        return x % 2;
    });
    return ret;
}
Casey
  • 41,449
  • 7
  • 95
  • 125
0

Use std::iterator_traits:

template<class ForwardIterator>
std::vector<typename std::iterator_traits<ForwardIterator>::value_type> get_odd(ForwardIterator data_begin, ForwardIterator data_end)
{
    ....
}
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160