指定类模板参数的要求

时间:2019-06-18 22:31:50

标签: c++ templates c++17 template-specialization

我有几个看起来像这样的类:

struct neg_inf {
    constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};

struct pos_inf {
    constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};

template<typename dX, class LowerBound, class UpperBound>
class limit {
    dX dx;
    UpperBound upperBound;
    LowerBound lowerBound;
    double step_size;

    limit( dX x, LowerBound lower, UpperBound upper, double step = 1 ) :
        dx{ x }, lowerBound{ lower }, upperBound{ upper }, step_size{ step }
    {}

    dX value() const { return dx; }
    LowerBound lower() const { return lowerBound; }
    UpperBound upper() const { return upperBound; }
    double step() const { return step_size; }            
};

到目前为止,这些类均按预期工作。现在,我想使用诸如std::enable_if_tstd::is_arithemtic std::is_same之类的条件来修改模板参数。

这些是实例化限制对象所需的条件。

  
      
  • dX必须至少为arithmeticnumerical
  •   
  • LowerUpper bound必须是numericalarithmeticneg_infpos_inf
  •   

例如,以下是有效的实例:

dX = 1st and can be any: int, long, float, double, /*complex*/, etc.

limit< 1st, 1st, 1st >     // default template

limit< 1st, 1st, pos_inf >  // these will be specializations
limit< 1st, 1st, neg_inf >
limit< 1st, pos_inf, 1st >
limit< 1st, neg_inf, 1st >
limit< 1st, pos_inf, pos_inf >
limit< 1st, neg_inf, neg_inf >
limit< 1st, neg_inf, pos_inf >
limit< 1st, pos_inf, neg_inf >

这些是实例化模板的有效条件。当UpperBoundLowerBound中的一个或两个都是infinity类型时,我打算对这个类进行部分专门化。当upperlower的边界是数字-算术类型时,常规或默认模板将处理它们。

我的问题是,template declaration库中班上的type_traits是什么样的?

2 个答案:

答案 0 :(得分:2)

将模板类型限制为类的一种方法是为SFINAE添加额外的参数:

template <typename dX, class LowerBound, class UpperBound, typename Enabler = void>
class limit;

然后提供适当的SFINAE专业化

template <typename dX, class LowerBound, class UpperBound>
class limit<dX,
            LowerBound,
            UpperBound,
            std::enable_if_t<my_condition<dX, LowerBound, UpperBound>::value>>
{
    // ...
};

因此,在您的情况下,my_condition应该类似于

template <typename dX, class LowerBound, class UpperBound>
using my_condition =
    std::conjunction<std::is_arithmetic<dX>,
                     std::disjunction<std::is_arithmetic<LowerBound>,
                                      std::is_same<LowerBound, neg_inf>,
                                      std::is_same<LowerBound, pos_inf>>,
                      std::disjunction<std::is_arithmetic<UpperBound>,
                                       std::is_same<UpperBound, neg_inf>,
                                       std::is_same<UpperBound, pos_inf>>
                      >;

另一种方法是static_assert

template <typename dX, class LowerBound, class UpperBound>
class limit
{
    static_assert(std::is_arithmetic<dX>::value, "!");
    static_assert(std::is_arithmetic<LowerBound>::value
                  || std::is_same<LowerBound, neg_inf>::value
                  || std::is_same<LowerBound, pos_inf>::value, "!");
    static_assert(std::is_arithmetic<UpperBound>::value
                  || std::is_same<UpperBound, neg_inf>::value
                  || std::is_same<UpperBound, pos_inf>::value, "!");
    // ...
};

答案 1 :(得分:2)

如果没有即将推出的C++20标准,我们将获得conceptsconstraints。考虑到这一点,我们可以声明我们自己的概念并摆脱SFINAE。

#include <limits>
#include <type_traits>

struct neg_inf {
    constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};

struct pos_inf {
    constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};

template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

template<typename T>
concept Bound = std::is_arithmetic_v<T>     || 
                std::is_same_v<T, neg_inf>  ||
                std::is_same_v<T, pos_inf>;

template<Arithmetic dX, Bound LowerBound, Bound UpperBound>
class limit {
 private:   
    dX dx;
    UpperBound upperBound;
    LowerBound lowerBound;
    double step_size;

public:
    limit() = default;
    limit( dX x, LowerBound lower, UpperBound upper, double step = 1 ) :
        dx{ x }, lowerBound{ lower }, upperBound{ upper }, step_size{ step }
    {}

    dX value() const { return dx; }
    LowerBound lower() const { return lowerBound; }
    UpperBound upper() const { return upperBound; }
    double step() const { return step_size; }            
};

LIVE DEMO