Специальная функция по значениям аргументов, известным на этапе компиляции в C++ [закрыто]

Вопрос или проблема

У меня есть функция с 2 аргументами: расстояние и радиус

float expensive_function(float distance, float radius) {
    return distance + radius;
}

Весь проект использует только 2 разных радиуса, и они оба известны на этапе компиляции (1.2f и 3.4f). Есть ли способ специализированно определить эту функцию, зная это? Я хотел бы что-то подобное, но это не компилируется:

template <float RADIUS>
float expensive_function(float distance) {
    return distance + RADIUS;
}

expensive_function<1.2f>(run_time_distance);
expensive_function<3.4f>(run_time_distance);

Компилятор говорит:
Кандидат по шаблону проигнорирован: недопустимый явно указанный аргумент для параметра шаблона 'RADIUS'
и
Нет подходящей функции для вызова 'expensive_function'

Я не очень знаком с программированием шаблонов в C++, поэтому я не ожидал, что это сработает. Я не знаю, возможно ли то, что я ищу. Я просто хочу убедиться, что компилятор сгенерирует наиболее производительный код для обоих случаев, плюс, на мой взгляд, это лучше с семантической точки зрения.

До C++20 вы не можете сделать float или double параметром шаблона, не являющимся типом.

Вам нужно использовать int и некоторую таблицу, чтобы это скомпилировать

#include <iostream>

constexpr float floats[] = {1.2,3.4};
template <int index>
void foo()
{
    constexpr float value = floats[index];
    std::cout << value << '\n';
}

int main()
{
    foo<0>();
}

Демо

Вы можете использовать перечисление, чтобы сделать это более читаемым

enum class float_values
{
    f1_2,
    f3_4
};

template <float_values index>
void foo()
{
    constexpr float value = floats[static_cast<int>(index)];
    std::cout << value << '\n';
}

Ответ или решение

Конечно! Давайте рассмотрим проблему специализированного шаблонизирования функции с аргументами, известными на этапе компиляции. В вашем случае, вам необходимо создать функцию expensive_function, которая принимает аргумент distance и использует два заранее известных значения радиуса: 1.2f и 3.4f.

К сожалению, до C++20 нельзя использовать float или double в качестве параметров шаблона, так как они не являются допустимыми не-типа параметрами. Однако вы можете обойти это ограничение, используя int в качестве параметра и создавая таблицу для сопоставления значений.

Вот один из способов сделать это:

  1. Создайте массив, который будет содержать ваши радиусы.
  2. Используйте int в качестве не-типа параметра шаблона и передавайте индекс радиуса в этот массив.

Вот пример реализации:

#include <iostream>

constexpr float radii[] = {1.2f, 3.4f};

template <int index>
float expensive_function(float distance) {
    static_assert(index >= 0 && index < sizeof(radii) / sizeof(radii[0]), "Index out of bounds");
    return distance + radii[index];
}

int main() {
    float run_time_distance = 5.0f;

    std::cout << "Result with radius 1.2: " << expensive_function<0>(run_time_distance) << std::endl; // Используем радиус 1.2
    std::cout << "Result with radius 3.4: " << expensive_function<1>(run_time_distance) << std::endl; // Используем радиус 3.4

    return 0;
}

В этом коде мы определяем массив radii, который содержит ваши фиксированные значения радиусов. Затем мы создаем шаблонную функцию expensive_function, принимающую индекс, и внутри функции мы обращаемся к массиву радиусов. Используя static_assert, мы можем гарантировать, что индекс находится в допустимых пределах, чтобы избежать ошибок во время компиляции.

Запуск этого кода выведет:

Result with radius 1.2: 6.2
Result with radius 3.4: 8.4

Такой подход может быть применим до C++20. Начиная с C++20, вы сможете использовать float в качестве не-типа параметра, что упростит работу. Пример кода мог бы выглядеть следующим образом:

#include <iostream>

template <float RADIUS>
float expensive_function(float distance) {
    return distance + RADIUS;
}

int main() {
    float run_time_distance = 5.0f;

    std::cout << "Result with radius 1.2: " << expensive_function<1.2f>(run_time_distance) << std::endl;
    std::cout << "Result with radius 3.4: " << expensive_function<3.4f>(run_time_distance) << std::endl;

    return 0;
}

Этот вариант намного чище и проще в использовании, так как вам не нужно использовать индексы и массивы.

Таким образом, подход с использованием индексов и массивов — это решение, которое следует применять до C++20, тогда как версия с использованием float возможна только начиная с C++20.

Оцените материал
Добавить комментарий

Капча загружается...