Constexpr, если альтернатива
Я хотел бы использовать constexpr if
для ветвления во время компиляции, но, похоже, он не поддерживается последним компилятором MSVC. Есть ли альтернатива следующему?:
template<typename T>
void MyFunc()
{
if constexpr(MeetsConditions<T>::value)
{
FunctionA<T>();
}
else
{
FunctionB<T>();
}
}
Вкратце: могу ли я имитировать constexpr if
, если он не поддерживается компилятором?
Ответы
Ответ 1
Одним из способов до С++ 17 является использование частичных специализаций шаблонов, как здесь:
template <template T, bool AorB>
struct dummy;
template <typename T, true>
struct dummy {
static void MyFunc() { FunctionA<T>(); }
}
template <typename T, false>
struct dummy {
static void MyFunc() { FunctionB<T>(); }
}
template <typename T>
void Facade() {
dummy<T, MeetsConditions<T>::value>::MyFunc();
}
Если вам нужно более двух специализаций - вы можете использовать перечисление или целочисленное значение и создавать специализации для всех необходимых перечислений.
Другой способ - использовать std::enable_if:
template <typename T>
std::enable_if<MeetsConditions<T>::value, void>::type
MyFunc() {
FunctionA<T>();
}
template <typename T>
std::enable_if<!MeetsConditions<T>::value, void>::type
MyFunc() {
FunctionB<T>();
}
Ответ 2
На самом деле существует несколько альтернатив (которые использовались задолго до начала if constexpr
).
Один из них отправляет теги:
template <class T>
void Function(std::true_type)
{
FunctionA<T>();
}
template <class T>
void Function(std::false_type)
{
FunctionB<T>();
}
template <class T>
void MyFunc()
{
Function<T>(std::integral_constant<bool, MeetsCondition<T>::value>{});
}
Еще одна черта:
template <bool B>
struct FunctionTraits;
template <>
struct FunctionTraits<true>
{
template <class T>
static void Call() { FunctionA<T>(); }
};
template <>
struct FunctionTraits<false>
{
template <class T>
static void Call() { FunctionB<T>(); }
};
template <class T>
void MyFunc()
{
FunctionTraits<MeetsCondition<T>::value>::Call<T>();
}
Ответ 3
Вы можете сделать это старомодным, проверенным и проверенным способом отправки меток:
template<typename T>
void MyFuncImpl(std::true_type) {
FunctionA<T>();
}
template<typename T>
void MyFuncImpl(std::false_type) {
FunctionB<T>();
}
template<typename T>
void MyFunc()
{
MyFuncImpl<T>(std::integral_constant<bool, MeetsConditions<T>::value>{});
}
Ответ 4
Если вы используете C++ 14 и Boost, рассмотрите возможность использования Hana. Реализовано с использованием Hana, это выглядит примерно так:
template<typename T>
void MyFunc()
{
hana::eval_if(MeetsConditions<T>::value,
[](auto) { FunctionA<T>(); },
[](auto _) { FunctionB<T>(_(exprThatWouldOtherwiseBeAnError)); }
);
}
Для конкретного случая обнаружения SFINAE и выполнения чего-либо только в этом случае это может быть так просто:
template<typename T>
void MyFunc()
{
auto maybeDoFunctionA = hana::sfinae([]() -> decltype((void) FunctionA<T>()) {
FunctionA<T>();
});
}
Ответ 5
if constexpr
- это функция С++ 17; перед С++ 17, начиная с С++ 11, вы можете использовать SFINAE с std::enable_if
template<typename T>
typename std::enable_if<true == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionA<T>(); }
template<typename T>
typename std::enable_if<false == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionB<T>(); }
- EDIT -
Если вы можете использовать только компилятор С++ 98, реализовать черты типа, которые работают как std::enable_if
, очень просто; см. следующий пример
template <bool, typename = void>
struct enableIf
{ };
template <typename T>
struct enableIf<true, T>
{ typedef T type; };
и функции становятся
template<typename T>
typename enableIf<true == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionA<T>(); }
template<typename T>
typename enableIf<false == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionB<T>(); }