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>(); }