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 шт):

Автор решения: HolyBlackCat

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')
→ Ссылка