Как сделать блок 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", если хотите.

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

SergeyA 22.04.2019 20:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
300
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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,"");

Полностью вопрос предпочтений, но я бы предпочел унаследовать bool_constant.

SergeyA 22.04.2019 20:54

@SergeyA Хороший вопрос, хотя обычно я просто объявляю псевдоним, потому что наследование, похоже, создает большую нагрузку на компилятор.

user7860670 22.04.2019 20:59

Интересное наблюдение о стоимости наследования и псевдонима во время компиляции. Никогда не проверял, как вы это измеряли?

SergeyA 22.04.2019 21:03

@SergeyA создание экземпляра нового типа обычно сложнее, чем разрешение псевдонима.

Guillaume Racicot 22.04.2019 21:05

@GuillaumeRacicot конечно, но мне интересно, поддается ли эффект количественному измерению.

SergeyA 22.04.2019 21:05

@SergeyA «мера», вероятно, была бы смелым термином, но в течение некоторого времени я боролся с ошибками компиляции, вызванными тем, что компилятору (gcc) не хватило ОЗУ на некоторой базе кода с относительно большим количеством шаблонов. И ограничение порождения новых типов шаблонов (особенно рекурсивным образом) оказалось наиболее эффективным способом противодействия им.

user7860670 22.04.2019 21:15
Ответ принят как подходящий

Одна вещь, которую вы можете сделать, это создать новый трейт, который будет 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, "");

@JeJo Это еще один правильный вариант. Есть довольно много способов освежевать эту кошку. Я просто не хотел вводить какие-либо фактические переменные.

NathanOliver 22.04.2019 21:02

@SergeyA Вау. Я все испортил. Спасибо за место. Я обновил ответ.

NathanOliver 22.04.2019 21:09

Вы можете определить 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...
    };
    

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

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

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

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

 constexpr static CustomCheck<T> check{};

Другие вопросы по теме