Felix
Felix

Reputation: 43

Container Iterator Template for one value type

you helped me before so here I am again with another question in hope for an answer.

I have a function that processes a range of std::complex<float> values. My initial attempt of a function definition was

// Example 1
using namespace std; // not in original code
Errc const& process(vector<complex<float>>::iterator begin, vector<complex<float>>::iterator end);

This however only works for vectors, not C-style arrays, std::arrays, ranges, etc.

After some fiddling, lots of Google and a little bit of luck, I managed to construct this:

// Example 2
using namespace std; // not in original code
template<
    template<typename T> class C,
    typename T,
    typename C<T>::iterator iterator
    >
Errc const& process(typename C<T>::iterator begin, typename C<T>::iterator end);

I am not even sure if that works but it compiles at least. What I think it does, is define a function that processes any container with an arbitrary value type. This would be a problem, as I can only process std::complex (or maybe similar complex floating point values) but not std::string for example.

What I would like to do:

// Example 3
using namespace std; // not in original code
template<
    template<typename complex<float>> class C,
    typename C<complex<float>>::iterator iterator
    >
Errc const& process(typename C<complex<float>>::iterator begin, typename C<complex<float>>::iterator end);

But this apparently is not the way to do this. I am about 60% percent sure I also messed something up in example 2.

Any help or hints are greatly appreciated. Thank you

Upvotes: 1

Views: 499

Answers (2)

Jarod42
Jarod42

Reputation: 217275

Your example 2 is nearly correct

template<
    template<typename T> class C,
    typename T,
    typename C<T>::iterator iterator // That is wrong
    >
Errc const& process(typename C<T>::iterator begin, typename C<T>::iterator end);

it should be:

template<template <typename> class C, typename T>
Errc const& process(typename C<T>::iterator begin, typename C<T>::iterator end);

But issue is that C/T are not deducible, and you have to call it like:

process<std::vector, std::complex<float>>(v.begin(), v.end());

And C-array and std::array doesn't match template <typename> class C neither (and std::vector has default allocator too :-/)

Simpler would just be

template<typename Iterator>
Errc const& process(Iterator begin, Iterator end);

possibly with some SFINAE

template <typename Iterator,
          std::enable_if_t<std::is_same_v<std::complex<float>,
                                          std::iterator_traits<Iterator>::value_type>, int> = 0>
Errc const& process(Iterator begin, Iterator end);

or C++20 requires:

template <typename Iterator>
Errc const& process(Iterator begin, Iterator end)
requires (std::is_same_v<std::complex<float>, std::iterator_traits<Iterator>::value_type);

If you want only contiguous sequences, you might use std::span

Errc const& process(std::span<std::complex<float>)

Upvotes: 3

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

You could use std::iterator_traits and compare the value_type of the iterator with the type you'd like to support:

#include <iterator>
#include <type_traits>

template<class It, std::enable_if_t<
    std::is_same_v<class std::iterator_traits<It>::value_type,
                   std::complex<float>
    >, int> = 0>
Errc const& process(It begin, It end) {
   //...
}

Alternative:

template<class It>
std::enable_if_t<std::is_same_v<class std::iterator_traits<It>::value_type,
                                std::complex<float>>, Errc const&>
process(It begin, It end) {
    //...
}

If SFINAE is not needed, static_assert:

template<class It>
Errc const& process(It begin, It end) {
    static_assert(
        std::is_same_v<class std::iterator_traits<It>::value_type,
                       std::complex<float>>);
    //...
}

Upvotes: 2

Related Questions