#c #templates
#c #шаблоны
Вопрос:
У меня есть следующий код
template <typename T>
auto CallMe(int param) -> void {
...
}
auto Test(int userInput) -> void {
int paramToUse = SomeFunction(userInput);
if (userInput == 0) {
CallMe<__int32>(paramToUse);
} else if (userInput == 1) {
CallMe<__int16>(paramToUse);
} else if (userInput == 2) {
CallMe<__int8>(paramToUse);
} else if (userInput == 3) {
...
}
Мой вопрос в том, что, поскольку все вызовы в CallMe()
точности одинаковы, кроме типа шаблона T
, есть ли в C способ присвоить тип переменной в каждой условной ветви и передать ее CallMe()
в одном месте, например
typename T;
if (userInput == 0) {
T = __int32;
} else if (userInput == 1) {
T = __int16;
} else if (userInput == 2) {
T = __int8;
} else if (userInput == 3) {
...
CallMe<T>(paramToUse);
Таким образом, если подпись CallMe
изменена, для обновления требуется только одна строка.
Комментарии:
1. Какова цель конечного возвращаемого типа в
auto CallMe(int param) -> void
?2. Нет цели. Просто мой стиль кодирования. Я предпочитаю указывать все типы, чтобы мне не приходилось угадывать при чтении.
3. Не
void CallMe(int param)
сделал бы то же самое?4. Это то же самое, но я предпочитаю новый завершающий стиль возврата. Таким образом, при наличии нескольких объявлений функций в заголовочном файле все имена функций начинаются с одного столбца.
5. __int32 выглядит странно
Ответ №1:
Как насчет:
decltype(CallMe<__int32>)* callable; // function pointer
// set the function address as necessary
if (userInput == 0) {
callable = CallMe<__int32>;
} else if (userInput == 1) {
callable = CallMe<__int16>;
} else if (userInput == 2) {
callable = CallMe<__int8>;
} else {
// ...
}
callable(paramToUse); // call it
Комментарии:
1. ? Ваш код — это точно мой первый код. И эти условия предназначены только для демонстрации.
2. @CrendKing Я этого не заметил. Смотрите обновленную версию
3. Есть ли какое-либо значение
__int32
в первой строке, или это просто обязательный заполнитель?4. @CrendKing просто заполнитель. Подойдет любой тип.
Ответ №2:
Время выполнения — условные псевдонимы типов невозможны, поскольку C является статически типизированным языком; это означает, что все типы должны быть разрешены во время компиляции.
В зависимости от вашего сценария вы можете найти решение, используя std::variant , однако вам придется либо передать новую переменную в вашу CallMe
функцию, либо заменить тип paramToUse
на variant .
Редактировать:
using VariantType = std::variant<std::monostate, std::uint8_t, std::uint16_t, std::uint32_t>;
void CallMe(const VariantType value) {
if (const auto uint8Value = std::get_if<std::uint8_t>(amp;value))
// use uint8Value in a meaningful way
else if (const auto uint16Value = std::get_if<std::uint16_t>(amp;value))
// ...
// ...
}
void function(const int userInput) {
const auto result = SomeFunction(userInput);
VariantType value {};
if (result == 0)
value = static_cast<std::uint32_t>(123);
else if (result == 1)
value = static_cast<std::uint16_t>(1234);
// more conditions
CallMe(value);
}
Комментарии:
1. Можете ли вы привести пример? Вы предлагаете изменить
CallMe
его на обычную, не шаблонную функцию и принять параметр std::variant?2. Добавлено, надеюсь, это поможет!
3. Я понимаю. По сути, вы заменяете шаблон переменной std::variant. Умный. К сожалению, real
CallMe
нуждается в этомT
в качестве шаблона для выполнения некоторыхif constexpr
трюков.4. Можете ли вы объяснить, зачем именно вам нужен этот тип? Например, если это для вызова базовой функции (например, вызова внешнего кода), этого можно избежать.
5. Обратите внимание, что все
CallMe<T>
они имеют один и тот же тип, что означает, что возможно сопоставление между временем выполненияuserInput
и временем компиляцииCallMe<T>
. Хотя ваш ответ, безусловно, является хорошим решением исходной проблемы OPs
Ответ №3:
поскольку все вызовы в
CallMe()
точности одинаковы, за исключением типа шаблонаT
, есть ли в C способ присвоить тип переменной в каждой условной ветви и передать ееCallMe()
в одном месте
Насколько я знаю, нет.
Ну … есть using
if (userInput == 0) {
using T = __int32;
} else if (userInput == 1) {
using T = __int16;
} ...
CallMe<T>(paramToUse); // T is out of scope
но область действия using
не распространяется на вызов функции.
Плохая часть вашего примера заключается в том, что тип определяется из известного значения во время выполнения ( userInput
), поэтому я не вижу решения лучше, чем то, которое вы используете.
Отличается, если вы можете передать userInput
как известное значение во время компиляции: в этом случае вы можете создать своего рода массив типов (используя std::tuple
, например) и выбрать тип в вызове.
Например, предположим userInput
, что значение параметра шаблона (так известно во время компиляции) и userInput
значение упорядочены, начиная с нуля (как в вашем вопросе), вы можете написать
template <std::size_t userInput>
void Test ()
{
using typeArray = std::tuple<__int32, __int16, __int8>;
CallMe<std::tuple_element_t<userInput, typeArray>>(paramToUse);
}
Комментарии:
1. Спасибо. Я заметил
using
, что у него проблема с областью действия, вот почему я создал этот вопрос. Я знаю об использовании целочисленных параметров шаблона. В библиотеке векторных классов их много. Ваш пример превращаетсяTest
в шаблонную функцию, чего я не могу сделать.2. @CrendKing — «Тест в функции шаблона, чего я не могу сделать» — поэтому я думаю, что единственный способ — это решение if-then-else-if (или также switch / case).