【问题标题】:Specifying the requirements for class template arguments指定类模板参数的要求
【发布时间】:2019-06-18 22:31:50
【问题描述】:

我有几个像这样的类:

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_arithemticstd::is_same 等条件来修改模板参数。

这些是实例化限制对象需要满足的条件。

  • dX 必须至少为 arithmeticnumerical
  • Lower & Upper 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 >

这些是实例化我的模板的有效条件。当UpperBound 和/或LowerBoundinfinity 类型时,我计划部分专业化该课程。当upperlower 边界是数值 - 算术类型时,通用或默认模板将处理它们。

我的问题是 template declarationtype_traits 库对我的班级来说会是什么样子?

【问题讨论】:

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


    【解决方案1】:

    将模板类型限制为类的一种方法是为 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, "!");
        // ...
    };
    

    【讨论】:

    • 我喜欢你的回答,但 SFINAE 是我仍在掌握的东西。我理解它的含义,我什至在一些很容易实现的情况下实现了它,但现在这个限制类是一只小野兽......语法变得冗长和一个错误的错误,Visual Studio 被它的编译器炸毁了错误大声笑!
    • 我一定要试试!
    • 您可能更喜欢static_assert。但 IMO 的“硬”/冗长部分主要是特征。
    • static_assert 可以工作;但我想我喜欢 SFINAE 方法。设置起来有点困难,但一旦启动并工作,应该会像魅力一样工作!
    • @FrancisCugler:错字已修复。
    【解决方案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

    【讨论】:

    • 我真的很喜欢这个答案,但目前我仅限于 c++17 并且无法访问 c++20,所以我必须满足于 SFINAE ......我非常感谢你为未来可能的设计实现提供答案。
    猜你喜欢
    • 1970-01-01
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 2022-01-10
    • 1970-01-01
    • 2015-05-05
    • 2014-02-03
    相关资源
    最近更新 更多