3

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.

JeJo
  • 30,635
  • 6
  • 49
  • 88
Unimportant
  • 2,076
  • 14
  • 19
  • 1
    If I understood correctly what you mean by `static_assert` block, looks like you can put those asserts into a template class and inheri `Foo` from from it. Let me know if you'd like code. – SergeyA Apr 22 '19 at 18:48

4 Answers4

4

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,"");
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • 2
    Totally a question of preference, but I would rather inherit from `bool_constant`. – SergeyA Apr 22 '19 at 18:54
  • @SergeyA Good point, though I would typically just declare an alias because inheritance seems to be putting more burden on compiler. – user7860670 Apr 22 '19 at 18:59
  • Interesting observation about compile-time costs of inheritance vs alias. Never checked, how did you measure that? – SergeyA Apr 22 '19 at 19:03
  • @SergeyA instantiating a new type is usually harder than resolving an alias – Guillaume Racicot Apr 22 '19 at 19:05
  • @GuillaumeRacicot sure, but I wonder if the effect is quantifiable. – SergeyA Apr 22 '19 at 19:05
  • 1
    @SergeyA "measure" would probably be a bold term, but for some time I've been fighting with compilation failures caused by compiler (gcc) running out of RAM on some relatively template-heavy code base. And limiting spawning of new template types (especially in recursive manner) proved to be the most efficient way to counter them. – user7860670 Apr 22 '19 at 19:15
3

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, "");
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
3

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...
    };
    
JeJo
  • 30,635
  • 6
  • 49
  • 88
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{};
JVApen
  • 11,008
  • 5
  • 31
  • 67