Как сделать блок static_assert повторно используемым в шаблонных классах? - PullRequest
3 голосов
/ 22 апреля 2019

Скажем, у меня есть шаблонный класс, который делает несколько 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», если хотите.

Ответы [ 4 ]

3 голосов
/ 22 апреля 2019

Одна вещь, которую вы можете сделать, это создать новую черту, которая является 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, "");
3 голосов
/ 22 апреля 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...
    };
    
3 голосов
/ 22 апреля 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,"");
1 голос
/ 22 апреля 2019

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

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

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

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

 constexpr static CustomCheck<T> check{};
...