Search code examples
c++templatesiteratortemplate-specializationoverload-resolution

How can I specialize an algorithm for iterators that point to complex values?


I am trying to write an algorithm that works on iterators (similar to the STL algorithms) however I need to write a specialization of the algorithm to act differently when the iterators point to complex values vs regular double values.

Here is a basic example:

#include <complex>
#include <iostream>
#include <vector>

using namespace std;

template <typename InputIt>
void DoSomething(InputIt first, InputIt last)
{
    cout << "Regular Double" << endl;

    for (; first != last; ++first)
    {
        cout << *first << endl;
    }
}

//// Specialize the template for containers holding complex values
//template <typename InputItToComplex>
//void DoSomething(InputItToComplex first, InputItToComplex last)
//{
//  cout << "Complex Double" << endl;
//
//  for (; first != last; ++first)
//  {
//      cout << *first << endl;
//  }
//}

int main()
{
    vector<double> values = { 1.5, 2.2, 3.1, 4.5, 5.1, 6.9, 7.1, 8.9 };

    // Call the regular template
    DoSomething(values.begin(), values.end());

    vector<complex<double>> cplx_values = { complex<double>{1.4, 2.1}, complex<double>{2.2, 3.5}, complex<double>{7.1, 9.1 } };

    // Need to call the complex specialized version of the template
    DoSomething(cplx_values.begin(), cplx_values.end());
}

How can I write the specialization so that it will automatically use the complex specialized version when I have a container of complex values? The commented out code above will obviously not work because it will just result in two ambiguous definitions.


Solution

  • You can use SFINAE and std::iterator_traits to constrain the "specialized" template. You also need a helper to check if the value_type returned by the iterator trait is a specializartion of std::complex. That code is

    template <class T, template <class...> class Template>
    struct is_specialization : std::false_type {};
    
    template <template <class...> class Template, class... Args>
    struct is_specialization<Template<Args...>, Template> : std::true_type {};
    

    And was written by Quentin here

    Using that you would have

    template <typename InputIt, 
              std::enable_if_t<!is_specialization<typename std::iterator_traits<InputIt>::value_type, std::complex>::value, bool> = true>
    void DoSomething(InputIt first, InputIt last)
    {
        cout << "Regular Double" << endl;
    
        for (; first != last; ++first)
        {
            cout << *first << endl;
        }
    }
    
    template <typename InputItToComplex, 
              std::enable_if_t<is_specialization<typename std::iterator_traits<InputItToComplex>::value_type, std::complex>::value, bool> = true>
    void DoSomething(InputItToComplex first, InputItToComplex last)
    {
        cout << "Complex Double" << endl;
    
        for (; first != last; ++first)
        {
            cout << *first << endl;
        }
    }