Как сделать блок static_assert повторно используемым в классах шаблонов?

Скажем, у меня есть класс шаблона, который создает несколько static_asserts:

template <class T>
class Foo
{
    static_assert(!std::is_const<T>::value,"");
    static_assert(!std::is_reference<T>::value,"");
    static_assert(!std::is_pointer<T>::value,"");

    //...<snip>...
}

Теперь предположим, что у меня есть другие классы шаблонов, которым нужно делать одни и те же утверждения.

Есть ли способ сделать блок static_assert многоразовым? "Функция static_assert", если хотите.


person Unimportant    schedule 22.04.2019    source источник
comment
Если я правильно понял, что вы подразумеваете под блоком static_assert, похоже, вы можете поместить эти утверждения в класс шаблона и наследовать от него Foo. Дайте мне знать, если вам нужен код.   -  person SergeyA    schedule 22.04.2019


Ответы (4)


Одна вещь, которую вы можете сделать, это создать новый трейт, который является conjunction черты, которые вы хотите проверить. Поскольку вы хотите отрицать все те черты, которые буквально означают

template<typename T>
using my_trait = std::conjunction<std::negation<std::is_const<T>>,
                                  std::negation<std::is_reference<T>>,
                                  std::negation<std::is_pointer<T>>>;

static_assert(my_trait<int>::value, "");

но необходимость использовать std::negation для каждой черты может быть мучительной. Вы можете избавиться от этого, используя std::disjunction, чтобы получить "или" из все черты, а затем просто отмените значение в статическом утверждении, как вы это делаете, что дает вам

template<typename T>
using my_trait = std::disjunction<std::is_const<T>,
                                  std::is_reference<T>,
                                  std::is_pointer<T>>;

static_assert(!my_trait<int>::value, "");
person NathanOliver    schedule 22.04.2019
comment
@JeJo Это еще один правильный вариант. Есть довольно много способов освежевать эту кошку. Я просто не хотел вводить какие-либо фактические переменные. - person NathanOliver; 22.04.2019
comment
@SergeyA Вау. Я все испортил. Спасибо за место. Я обновил ответ. - person NathanOliver; 22.04.2019

Вы можете просто объединить необходимые черты в одну с описательным именем:

template<typename T> using
is_fancy = ::std::integral_constant
<
    bool
,   (not std::is_const<T>::value)
    and
    (not std::is_reference<T>::value)
    and
    (not std::is_pointer<T>::value)
>;

и использовать его позже:

static_assert(std::is_fancy<T>::value,"");
person user7860670    schedule 22.04.2019
comment
Полностью вопрос предпочтений, но я бы скорее унаследовал от bool_constant. - person SergeyA; 22.04.2019
comment
@SergeyA Хороший вопрос, хотя обычно я просто объявляю псевдоним, потому что наследование, похоже, создает большую нагрузку на компилятор. - person user7860670; 22.04.2019
comment
Интересное наблюдение о стоимости наследования и псевдонима во время компиляции. Никогда не проверял, как вы это измеряли? - person SergeyA; 22.04.2019
comment
@SergeyA создание экземпляра нового типа обычно сложнее, чем разрешение псевдонима. - person Guillaume Racicot; 22.04.2019
comment
@GuillaumeRacicot конечно, но мне интересно, поддается ли эффект количественному измерению. - person SergeyA; 22.04.2019
comment
Мера @SergeyA, вероятно, была бы смелым термином, но в течение некоторого времени я боролся с ошибками компиляции, вызванными тем, что компилятору (gcc) не хватает ОЗУ в некоторой кодовой базе с относительно большим количеством шаблонов. И ограничение порождения новых типов шаблонов (особенно рекурсивным образом) оказалось наиболее эффективным способом противодействия им. - person user7860670; 22.04.2019

Я видел несколько хороших ответов, используя союз. К сожалению, их действительно трудно отлаживать. Однажды мне пришлось отлаживать проблему с моим классом, в котором говорилось: требования выполнены. Это был слишком длинный список, чтобы понять. Наконец, я скопировал все базовые проверки одну за другой.

Когда это возможно, я предпочитаю разделять их:

template<typename T>
struct CustomCheck {
     static_assert(check<T>);
      // ...
 };

В вашем классе вам нужно только создать его экземпляр, чтобы получить проверку и подробную ошибку:

 constexpr static CustomCheck<T> check{};
person JVApen    schedule 22.04.2019

Вы можете определить constexpr bool, который выполняет оценку во время компиляции:

template<typename T>
inline constexpr bool is_okay_type = !std::is_const<T>::value &&
                                     !std::is_reference<T>::value &&
                                     !std::is_pointer<T>::value;

Тогда либо:

  1. используйте его непосредственно static_assert<>, как в вашем примере:

    template<typename T> class MyClass
    {
        static_assert(is_okay_type<T>, "message");
    public:
        //...code...
    };
    
  2. или вы можете сделать условное создание экземпляра класса шаблона, в зависимости от аргумента шаблона.

    template<typename Type, typename Enable = void> class Class1;
    
    template<typename Type>
    class Class1<Type, std::enable_if_t<is_okay_type<Type>> >
    {
        //...code...
    };
    
person JeJo    schedule 22.04.2019