What does static_assert do and what are you using it for?

Could you give an example where static_assert(...) ('C ++ 11') will elegantly solve the problem?

I am familiar with the assert(...) runtime. When should I prefer static_assert(...) rather than regular assert(...) ?

Also, in boost is there something called BOOST_STATIC_ASSERT , is it the same as static_assert(...) ?

+108
c ++ debugging assert c ++ 11 static-assert
Oct 30 '09 at 3:35
source share
8 answers

Over my head ...

 #include "SomeLibrary.h" static_assert(SomeLibrary::Version > 2, "Old versions of SomeLibrary are missing the foo functionality. Cannot proceed!"); class UsingSomeLibrary { // ... }; 

Assuming SomeLibrary::Version declared as a static const, not #define d (as you would expect in the C ++ library).

The contrast with the need to compile SomeLibrary and your code, link everything and run the executable file only to find out that you spent 30 minutes compiling an incompatible version of SomeLibrary .

@Arak, in response to your comment: yes, you can static_assert just sit anywhere, from the look:

 class Foo { public: static const int bar = 3; }; static_assert(Foo::bar > 4, "Foo::bar is too small :("); int main() { return Foo::bar; } 
 $ g ++ --std = c ++ 0x a.cpp
 a.cpp: 7: error: static assertion failed: "Foo :: bar is too small :("
+71
Oct 30 '09 at 3:43
source share

A static statement is used to execute statements at compile time. When a static statement fails, the program simply fails to compile. This is useful in different situations, for example, if you implement some functions using code that is critically dependent on an unsigned int object that has exactly 32 bits. You can put a static statement like this

 static_assert(sizeof(unsigned int) * CHAR_BIT == 32); 

in your code. On another platform with an unsigned int type with different sizes, compilation will fail, drawing the attention of the developer to the problem part of the code and advising them to re-implement or re-test it.

In another example, you may need to pass some integer value as a void * pointer to a function (hack, but useful from time to time), and you want to make sure that the integral value fits into the pointer

 int i; static_assert(sizeof(void *) >= sizeof i); foo((void *) i); 

You may need char type activation

 static_assert(CHAR_MIN < 0); 

or integral division with negative values โ€‹โ€‹is rounded to zero

 static_assert(-5 / 2 == -2); 

And so on.

In many cases, runtime statements can be used in place of static statements, but runtime statements work only at runtime and only when control passes that statement. For this reason, an unsuccessful statement at runtime may remain inactive, undetected for extended periods of time.

Of course, the expression in the static statement must be a compile-time constant. This cannot be a runtime value. For runtime values, you have no choice other than the usual assert .

+123
30 Oct '09 at 3:47
source share

I use it to make sure my assumptions about compiler behavior, headers, libraries, and even my own code are correct. For example, here I check that the structure has been correctly packed to the expected size.

 struct LogicalBlockAddress { #pragma pack(push, 1) Uint32 logicalBlockNumber; Uint16 partitionReferenceNumber; #pragma pack(pop) }; BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6); 

In the wrapper of the stdio.h fseek() class, I took several shortcuts with enum Origin and make sure that these shortcuts match the constants defined by stdio.h

 uint64_t BasicFile::seek(int64_t offset, enum Origin origin) { BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET); 

You should prefer static_assert over assert when the behavior is determined at compile time rather than at run time, such as the examples above. An example where this is not the case would include checking the code and return code.

BOOST_STATIC_ASSERT is a pre-C ++ 0x macro that generates illegal code if the condition is not met. The intentions are the same, although static_assert standardized and can provide better compiler diagnostics.

+12
Oct 30 '09 at 3:46
source share

BOOST_STATIC_ASSERT is a cross-platform wrapper for static_assert .

I am currently using static_assert to enforce "Concepts" in a class.

Example:

 template <typename T, typename U> struct Type { BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value); BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer); /* ... more code ... */ }; 

This will result in a compile-time error if any of the above conditions are not met.

+9
Jan 12 '10 at 12:18
source share

One use of static_assert can be to make the structure (i.e., the interface with the outside world, such as a network or file) exactly what you expect. This will catch cases when someone adds or modifies a member from the structure without realizing the consequences. static_assert will pick it up and warn the user.

+7
Oct 30 '09 at 3:43
source share

In the absence of concepts, you can use static_assert for simple and readable compilation type checking, for example, in templates:

 template <class T> void MyFunc(T value) { static_assert(std::is_base_of<MyBase, T>::value, "T must be derived from MyBase"); // ... } 
+3
Sep 14 '15 at 20:54
source share

This does not directly answer the original question, but does an interesting study of how to apply these compile-time checks before C ++ 11.

Chapter 2 (Section 2.1) โ€œModern C ++ Designโ€ by Andrei Alexanderscu implements this idea of โ€‹โ€‹compile-time statements such as

 template<int> struct CompileTimeError; template<> struct CompileTimeError<true> {}; #define STATIC_CHECK(expr, msg) \ { CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

Compare the macro STATIC_CHECK () and static_assert ()

 STATIC_CHECK(0, COMPILATION_FAILED); static_assert(0, "compilation failed"); 
+2
Jun 02 '14 at 3:47
source share

static_assert can be used to prohibit the use of the delete keyword as follows:

#define delete static_assert(0, "The keyword \"delete\" is forbidden.");

Every modern C ++ developer might want to do this if he or she wants to use a conservative garbage collector, using only the es class and struct , which overload the new operator to call a function that allocates memory in the conservative heap of a conservative garbage collector that can be initialized and created, by calling some function that does this at the beginning of the main function.

For example, every modern C ++ developer who wants to use the Boehm-Demers-Weiser conservative garbage collector will write at the beginning of the main function:

GC_init();

And in each class and struct overload operator new as follows:

 void* operator new(size_t size) { return GC_malloc(size); } 

And now that operator delete no longer needed, since the Boehm-Demers-Weiser conservative garbage collector is responsible for both freeing up and freeing every block of memory when it is no longer needed, the developer wants to disable delete keyword.

One way is to overload the delete operator this way:

 void operator delete(void* ptr) { assert(0); } 

But this is not recommended because a modern C ++ developer will know that he / she mistakenly called delete operator at run time, but it is better to know this soon at compile time.

Therefore, in my opinion, the best solution to this scenario is to use static_assert , as shown at the beginning of this answer.

Of course, this can also be done with BOOST_STATIC_ASSERT , but I think static_assert better and should always be preferred.

-one
Aug 23 '19 at 22:45
source share



All Articles