【问题标题】:Constexpr if alternative替代方案
【发布时间】:2017-04-24 11:55:00
【问题描述】:

我想在编译时使用constexpr if 进行分支,但最新的MSVC 编译器似乎不支持它。是否有以下替代方案?:

template<typename T>
void MyFunc()
{
    if constexpr(MeetsConditions<T>::value)
    {
        FunctionA<T>();
    }
    else
    {
        FunctionB<T>();
    }
}

简而言之:当编译器不支持constexpr if时,我可以模拟它吗?

【问题讨论】:

标签: c++ template-meta-programming c++17


【解决方案1】:

C++17 之前的一种方法是使用部分模板特化,如下所示:

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

如果您需要超过 2 个特化 - 您可以使用枚举或整数值,并对所有需要的枚举进行特化。

另一种方法是使用 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】:

    你可以用老式的、久经考验的标签调度方式:

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

    【讨论】:

      【解决方案3】:

      如果您使用 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>();
          });
      }
      

      【讨论】:

        【解决方案4】:

        确实有几种替代方案(早在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>();
        }
        

        【讨论】:

          【解决方案5】:

          if constexpr 是 C++17 的一个特性;在 C++17 之前,从 C++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>(); }
          

          -- 编辑--

          如果你只能使用 C++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>(); }
          

          【讨论】:

          • SFINAE 不需要 C++11,它一直存在于 C++ 中。
          • @Angew - 你的权利:是 std::enable_if,它可以从 C++11 获得,而不是 SFINAE;谢谢;答案已修改
          • 如果 C++11 不是一个选项,总会有 boost::enable_if
          • @Angew - 是的,但我更喜欢给出避免非标准 api 的答案;否则,请实现 std::enable_if 之类的类型特征,它是如此简短和简单……感谢您的建议;答案已修改
          【解决方案6】:

          我最近陷入了困境...... 所以,我创建了一个很小的 ​​if_constexpr library 用于将一些 c++17 代码反向移植到 c++14。

          #include <if_constexpr.hpp>
          
          template<typename T>
          constexpr void MyFunc()
          {
              using namespace ic;
              if_<MeetsConditions<T>::value>([] {
                  FunctionA<T>();
              }, else_([] {
                  FunctionB<T>();
              }));
          }
          

          【讨论】:

            猜你喜欢
            • 2012-10-05
            • 2010-09-10
            • 2012-12-05
            • 2013-11-22
            • 2020-11-23
            • 2013-08-07
            • 2015-09-11
            • 2021-06-14
            • 1970-01-01
            相关资源
            最近更新 更多