Задание требований к аргументам шаблона класса - PullRequest
2 голосов
/ 19 июня 2019

У меня есть несколько классов, которые выглядят так:

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_t, std::is_arithemtic std::is_same.

Это условия, которые необходимо выполнить для создания экземпляра объекта ограничения.

  • dX должно быть не менее arithmetic и numerical
  • Lower & Upper bound должно быть либо numerical, arithmetic или neg_inf, либо pos_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 и или LowerBound имеют тип infinity.Когда границы upper и lower являются числово-арифметическими типами, шаблон общего или стандартного формата будет обрабатывать их.

Мой вопрос заключается в том, как будет выглядеть template declaration для моего класса с type_traitsбиблиотека

Ответы [ 2 ]

2 голосов
/ 20 июня 2019

С учетом следующего стандарта C++20 мы получим концепции и ограничения . Имея это в виду, мы можем заявить о наших собственных концепциях и избавиться от 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

2 голосов
/ 19 июня 2019

Один из способов ограничить тип шаблона классом - добавить дополнительный параметр для 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, "!");
    // ...
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...