Учитывая параметр функции типа Container: Iterator, как достичь определенной перегрузки для определенных типов T?
В свободное время я занимаюсь обработкой сигналов и пишу собственную библиотеку DSP в качестве учебного упражнения. Я написал функцию, которая вычисляет дискретное преобразование Фурье вектора. Существует две перегрузки функции: одна для const std::vector<float> &
, а другая для std::vector<std::complex<float>>
.
Я хотел бы сделать это более общим. Вместо вектора функция должна принимать пару универсальных итераторов произвольного доступа. Компилятор должен затем вывести из итераторов, является ли это сложные или реально значимые данные и выбрать правильную перегрузку.
Это должно дать вам идею,
//compute the discrete fourier transform in-place for a complex valued input
template<template<typename T> class Container, typename T, class RandomAccessIt>
void fft(
typename Container<std::complex<T>>::RandomAccessIt first,
typename Container<std::complex<T>>::RandomAccessIt last
)
{
...
}
//calculate the discrete fourier transform for a real valued input
template<template<typename T> class Container1,
template<std::complex<T>> class Container2,
typename T,
class RandomAccessIt1,
class RandomAccessIt2>
void fft(
typename Container1<T>::RandomAccessIt1 first,
typename Container1<T>::RandomAccessIt1 last,
typename Container2<std::complex<T>>::RandomAccessIt2 out_first
)
{
...
fft(...); //call the complex version
...
}
Но, как вы можете видеть, я действительно не знаю, что я делаю с шаблонами. Как мне заставить это работать? Если это невозможно как есть, то почему?
Правка:
Мне больше всего нравится if constexpr
-подход, так как он решает проблему довольно элегантно
template<typename T>
struct is_complex_t : public std::false_type {};
template<typename T>
struct is_complex_t<std::complex<T>> : public std::true_type {};
template<typename RandomAccessIt>
void fft(RandomAccessIt first, RandomAccessIt last)
{
using Number_t = typename std::iterator_traits<RandomAccessIt>::value_type;
if constexpr(!is_complex_t<Number_t>::value)
{
//math trickery to transform the real input data to complex-valued data
...
}
... //the FFT itself
if constexpr(!is_complex_t<Number_t>::value)
{
//math trickery to properly format the frequency domain data
...
}
}
Тем не менее, с тех пор я понял, что поскольку две перегрузки имеют разное число параметров, я даже не нужно никакого умного метапрограммирования :
//compute the discrete fourier transform in-place for a complex valued input
template<typename RandomAccessIt>
void fft(RandomAccessIt first, RandomAccessIt last)
{
//...
}
//calculate the discrete fourier transform for a real valued input
template<typename RandomAccessIt1, typename RandomAccessIt2>
void fft(
RandomAccessIt1 first, RandomAccessIt1 last,
RandomAccessIt2 out_first
)
{
//...
//fft(...); //call the complex version
//...
}
1 ответ:
Ну, просто отбросьте параметры контейнера и шаблона T и возьмите только тип итератора; это то, что делают все стандартные библиотечные алгоритмы (например, в
<algorithm>
). Затем используйтеstd::iterator_traits
чтобы получитьT
в качестве итератораvalue_type
.Теперь, для вашей специализации, вы можете использовать один из:
std::enable_if_t
- помеченная отправка
- constexpr-if-so у вас будет одна и та же функция с двумя областями для двух случаев.
В этом вопросе:
Если constexpr вместо тега dispatch
Вы увидите пример того, как использовать второй вариант (tagged dispatch) в вопросе, и пример того, как преобразовать его в constexpr-if в одном из ответов.