Deduction guide для функции
У меня есть код для интегрирования систем дифференциальных уравнений. Он представляет из себя шаблонную функцию с параметром - размерностью системы, имеющим тип int. Выглядит это так:
template<int n> std::array<double, n> operator+(const std::array<double, n>& a,
const std::array<double, n>& b)
{ // Вспомогательная функция
std::array<double, n> ret;
for (int i = 0; i < n; i++) ret[i] = a[i] + b[i];
return ret; // NRVO
}
template<int n> std::array<double, n> operator*(double h,const std::array<double, n>& b)
{ // Вспомогательная функция
std::array<double, n> ret;
for (int i = 0; i < n; i++) ret[i] = h * b[i];
return ret; // NRVO
}
template<int n> std::vector<std::array<double, n>>
rk4(std::function<std::array<double, n>(double, const std::array<double, n>&)> f,
double t0, const std::array<double, n>& x0, double tend, double h)
{ // Функция для решения системы
if (tend < t0) return {};
size_t points = static_cast<size_t>(1 + floor((tend - t0) / h));
std::vector<std::array<double, n>> ret{ points };
ret[0] = x0;
double t = 0;
for (size_t i = 1; i < points; i++)
{ // Метод Рунге-Кутты 4 порядка с пост. шагом
auto k1 = f(t, ret[i-1]);
auto k2 = f(t + h / 2, ret[i - 1] + (h / 2 ) * k1);
auto k3 = f(t + h / 2, ret[i - 1] + (h / 2 ) * k2);
auto k4 = f(t + h, ret[i - 1] + h * k3);
ret[i] = ret[i - 1] + (h / 6) * (k1 + 2*k2 + 2*k3 + k4);
}
return ret;
}
Вот как я его использую:
std::array<double, 2> test1(double t, const std::array<double, 2>& x)
{ // функция, считающая правую часть системы размерности 2
return { x[1], -x[0] };
}
где-то в коде:
auto result = rk4<2>(test1, 0, { 1,0 }, 6.28*10, 0.05);
Убрать двойку в угловых скобках нельзя, компилятор не поймёт, что n равно двум. Хотя она здесь явно лишняя в том смысле, что ничему кроме 2 n равно быть не может. Я попытался написать для rk4 deduction guide, чтобы размерность выводилась из размера третьего аргумента:
template <typename F,typename T0, typename X0, typename Tend, typename H>
rk4(F, T0, X0, Tend, H)->rk4<std::tuple_size<X0>::value>;
но это не работает. Мой вопрос: возможно ли здесь написать deduction guide и если да, то как?
Ответы (1 шт):
Deduction guides бывают только для классов. Но ваша в любом случае бы не сработала, потому что вот такое не скомпилируется: template <typename T> void foo(T) {} foo({1,2,3});
Почему rk4(test1, 0, {1, 0}, 6.28*10, 0.05); не работает?
Здесь шаблонный параметр int n теоретически можно было бы вывести либо из test1, либо из {1, 0}.
Из test1 он не вывелся как минимум потому, что неявные преобразования запрещены для аргументов функции, участвующих в выводе шаблонных параметров. Это вызвало бы ошибку, даже если бы вывод из {1, 0} сработал.
Если использовать {test1} вместо test1, то неявного преобразования не будет, но по такому аргументу шаблонный параметр все равно не выведется (причину см. ниже), несмотря даже на deduction guide, который есть у std::function.
А из {1, 0} шаблонный параметр не вывелся потому, что похоже, что аругменты, являющиеся списками в фигурных скобках, участвуют в выводе шаблонных параметров только если заранее известно, что соответствующий параметр функции (после отбрасывания cv-квалификаторов и/или ссылочности) является массивом или std::initializer_list.
Возможное решение - сделать так, чтобы один их этих двух аргументов был нужного типа:
Либо
test1заменить наstd::function{test1}(deduction guide, ура!).Либо
{1, 0}заменить наstd::array{1.0, 0.0}. А такжеtest1на{test1}чтобы убрать неявное преобразование.
В любом случае тип шаблонного параметра нужно изменить с int на std::size_t, иначе вылезает это:
candidate template ignored: substitution failure: deduced non-type template argument does not have the
same type as the corresponding template parameter ('unsigned long' vs 'int')