Вопрос или проблема
У меня есть функция с 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
в качестве параметра и создавая таблицу для сопоставления значений.
Вот один из способов сделать это:
- Создайте массив, который будет содержать ваши радиусы.
- Используйте
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.