【问题标题】:enable_if method specializationenable_if 方法特化
【发布时间】:2011-10-01 10:37:08
【问题描述】:
template<typename T>
struct A
{
    A<T> operator%( const T& x);
};

template<typename T>
A<T> A<T>::operator%( const T& x ) { ... }

我如何使用 enable_if 对任何浮点类型 (is_floating_point) 进行以下特化?

template<>
A<float> A<float>::operator%( const float& x ) { ... }

编辑: 这是我想出的答案,与下面发布的答案不同...

template<typename T>
struct A
{
    T x;

    A( const T& _x ) : x(_x) {}

    template<typename Q>
    typename std::enable_if<std::is_same<Q, T>::value && std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(fmod(x, right));
    }

    template<typename Q>
    typename std::enable_if<std::is_convertible<Q, T>::value && !std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(x%right);
    }
};

就像下面的海报所说,使用 enable_if 可能不适合这个问题(它很难阅读)

【问题讨论】:

    标签: c++ boost c++11 template-specialization tr1


    【解决方案1】:

    当您想为更具体的参数类型优化行为时,请使用重载而不是显式特化。它更易于使用(更少惊喜)且更强大

    template<typename T>
    struct A
    {
        A<T> operator%( const T& x) { 
          return opModIml(x, std::is_floating_point<T>()); 
        }
    
        A<T> opModImpl(T const& x, std::false_type) { /* ... */ }
        A<T> opModImpl(T const& x, std::true_type) { /* ... */ }
    };
    

    一个使用 SFINAE (enable_if) 的例子,你似乎很好奇

    template<typename T>
    struct A
    {
        A<T> operator%( const T& x) { 
          return opModIml(x); 
        }
    
        template<typename U, 
                 typename = typename 
                   std::enable_if<!std::is_floating_point<U>::value>::type>
        A<T> opModImpl(U const& x) { /* ... */ }
    
        template<typename U, 
                 typename = typename 
                   std::enable_if<std::is_floating_point<U>::value>::type>
        A<T> opModImpl(U const& x) { /* ... */ }
    };
    

    当然更丑。我认为没有理由在这里使用enable_if。太过分了。

    【讨论】:

    • 感谢您的回答,我确实喜欢您的解决方案,但我不同意您为什么认为模板专业化是错误的。如果可以使用 enable_if 专门针对多种类型,我仍然想知道如何
    • @Dave 因为您可以改用重载。如果你有一个参数并且你想专门针对参数类型,重载会更好。不,你不能“专攻enable_if”。你的意思是超载。我将添加一个重载和使用 SFINAE 的示例。
    • 这听起来可能很愚蠢,但我不喜欢 opModImpl 在那里,即使它是私有的。此外,我不喜欢在那里创建一个额外的临时文件的想法,即使优化器应该摆脱它。 C++0x 应该只包含一种让多个专业化首先使用相同定义的方法
    • @Dave :我所知道的每一个大量使用 TMP 的库都尽可能使用这种精确的技术而不是 SFINAE 解决方案。也就是说,你的疑虑是没有根据的。 ;-]
    • @Johannes 您的 enable_if 解决方案无法为我编译... error C4519: default template arguments are only allowed on a class template error C2535: 'A&lt;T&gt; A&lt;T&gt;::opModImpl(const U &amp;)' : member function already defined or declared
    【解决方案2】:

    您也可以像这样使用默认的布尔模板参数:

    template<typename T>
    struct A
    {
        T x;
    
        A( const T& _x ) : x(_x) {}
    
        template<bool EnableBool = true>
        typename std::enable_if<std::is_floating_point<T>::value && EnableBool, A<T> >::type 
        operator% ( const T& right ) const
        {
            return A<T>(fmod(x, right));
        }
    
        template<bool EnableBool = true>
        typename std::enable_if<!std::is_floating_point<T>::value && EnableBool, A<T> >::type 
        operator% ( const T& right ) const
        {
            return A<T>(x%right);
        }
    };
    

    【讨论】:

      【解决方案3】:

      使用 C++20

      您可以通过添加 requires 来限制相关模板功能来实现:

      template<typename Q> // the generic case, no restriction
      A<T> operator% ( const Q& right ) const {
          return A<T>(std::fmod(x, right));
      }
      
      template<typename Q> requires std::is_integral_v<T> && std::is_integral_v<Q>
      A<T> operator% ( const Q& right ) const {
          return A<T>(x % right);
      }
      

      requires 子句得到一个constant expression,其计算结果为truefalse,从而决定是否在重载决议中考虑此方法,如果 requires 子句为真,则该方法优于另一个没有 requires 子句的,因为它更专业。

      代码:https://godbolt.org/z/SkuvR9

      【讨论】:

        猜你喜欢
        • 2012-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-10
        • 2012-04-05
        • 1970-01-01
        相关资源
        最近更新 更多