How to make static_assert block re-usable in template classes?

一世执手 提交于 2019-12-10 09:55:50

问题


Say I have a template class that makes multiple 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>...
}

Now say I have more template classes that need to make the same asserts.

Is there a way to make a static_assert block reusable? A "static_assert function" if you will.


回答1:


One thing you can do is build a new trait that is a conjunction of the traits you want to check. Since you want the negation of all of those traits that would literally translate to

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

but having to use std::negation for every trait is/can be a pain. You can get rid of that though using std::disjunction to get an "or" of all the traits and then just negate the value in the static assert like you do which gives you

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



回答2:


You can just combine required traits into one with descriptive name:

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

and use it later:

static_assert(std::is_fancy<T>::value,"");



回答3:


I've seen a few good answers, using the conjunction. Unfortunately, these are really hard to debug. I've once had to debug an issue with my class stating: requirements met. This was a too long list to understand. I finally copied all underlying checks one by one.

When possible, I like to keep them split:

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

In your class, you only have to instantiate it to get the checking and a detailed error:

 constexpr static CustomCheck<T> check{};



回答4:


You can define a constexpr bool which does the evaluation at compile time:

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

Then either:

  1. use it directly static_assert<>, as you did in your example:

    template<typename T> class MyClass
    {
        static_assert(is_okay_type<T>, "message");
    public:
        //...code...
    };
    
  2. or you can make conditional instantiation of the template class, depending on the template argument.

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


来源:https://stackoverflow.com/questions/55799464/how-to-make-static-assert-block-re-usable-in-template-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!