C++11 introduces this new type of assertion which checks an expression at compile time. This is different from regular “assert” statement, which works at runtime. When the expression in static_assert evaluates to value “False” then, it the program will not compile.
In the following example, the compiler executes a function “funda” at compile time and checks if it is returning integer value “1”. Ultimately, the expression fails and compiler throws error.
#include <iostream> //main header
using namespace std; //namespace
constexpr int funda()
{
return 0;
}
int main()
{
static_assert(funda()==1, "This is not equal");
return 0;
}
The output is:
When compiler cannot check the expression
For static_assert to work properly, the assert condition must be a constant expression. This means, if the compiler is not able to evaluate the expression, then static_assert will not work.
Unlike previous example, now the the function “funda” do not have “constexpr” keyword. Therefore, compiler shall not be able evaluate this anymore.
int funda() //Not constexpr
{
return 0;
}
The output is:
Limitation in C++11 with constexpr
In C++11, a constexpr function must be of a single line and that line can be only one return statement. However, this limitation is removed in C++14. Finally, the following function cannot compile in C++11 but will compile in C++14.
#include <iostream> //main header
using namespace std; //namespace
constexpr int funda()
{
constexpr int iy = 8; //Line no. 1
return iy; //Line no. 2
}
int main()
{
static_assert(funda() == 1, "This is not equal");
return 0;
}
Output in C++11 (compiler error)
Output in C++14 (static_assert failed error)
Using decltype for templates
Compilers can execute decltype on a template and also deduces its type. However, as the case in above example, the type deduction should be possible during compilation. For example, in following program, the return type of template function “funda” is T and this is clearly deducible by checking return value “tx” type. Here, the template function initialization has argument of type “integer” and therefore same shall be the return type.
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
T funda(T tx) // deduction of T needed
{
return tx;
}
int main()
{
static_assert(is_same<decltype(funda(4)), char> (),
"This is not equal");
return 0;
}
The output is compiler static_assert fail message:
Using decltype with reference
Along with the datatype, the decltype keyword also knows if the given type is reference or not. The program can pass decltype’s return value to a template function. In such case, the compiler first instantiate the template with type and then checks if this is a reference. The following program uses the template functions from std namespace, (is_reference etc) with return value of decltype.
#include <iostream> //main header
using namespace std; //namespace
int main()
{
int ix = 9; // ix is not a reference
int &iy = ix; // iy is a reference
int &iz = ix; // iz is l-value reference
int &&ia = 4; // ia is r-value reference
static_assert(is_reference<decltype(ix)> (),
"This is not reference");
static_assert(is_reference<decltype(iy)> () ,
"This is not reference");
static_assert(is_lvalue_reference<decltype(iz)> () ,
"This is Lvalue reference");
static_assert(is_rvalue_reference<decltype(ia)> () ,
"This is Rvalue reference");
return 0;
}
Here, only the first static_assert fails because variable ix is not reference and it is_reference return false. The output is:
Using sizeof( ) at compile-time
Just like, decltype, the compiler can also evaluate sizeof ( ). The advantage with sizeof is that it can also work with a template parameter as argument. In the following program, the parameter T in function template “fun” deduces as integer type. Therefore, sizeof(T) shall return sizeof(int), which is value 4. Everything happens in compile time. Specifically, deduction of argument and execution of sizeof is executed inside static_assert.
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(T tx)
{
static_assert(sizeof(tx) != 4,
"This is int, if it fails");}
int main()
{
auto ax = 9;
fund(ax);
return 0;
}
The sizeof(integer) != 4 is false, therefore, the compiler generates following error.
Another format of static_assert with no message
The static_assert do not require message. The following example, shows how to use this overload.
template<typename T>
void funda(T tx)
{
static_assert(sizeof(tx) != 4);
}
Using auto return type in static_assert
When a function’s return type is auto then the compiler can deduce the actual type with return statement. The type deduction rules of auto applies.
In following program, the return statement in function “fun” returns the value of type std::string. Therefore, return type “auto” deduces to type std::string. Finally, due to presence of keyword const and ampersand, the return type of function becomes “const std::string&”.
Since, everything happens at compile time and is_same is also template function, therefore, the static_assert works as shown in the output.
#include <iostream> //main header
using namespace std; //namespace
const auto& funda()
{
static std::string test{ "hello funda!" };
return test;
}
int main()
{
static_assert(std::is_same<decltype(funda()),
const std::string&>(), ""); //Pass
static_assert(std::is_same<decltype(funda()),
const std::string>(), ""); //Fails
return 0;
}
The output will be straightforward in c++14. The compiler generates following message.
However, in C++11, the auto type deduction for return type cannot happen without trailing return type. Therefore, the compiler throws another error.
To correct this, firstly, the return type should be “auto” and not “const auto&”. Secondly, the trailing return type has to be specified as shown below.
auto funda() -> const std::string&
{
static std::string test{ "hello funda!" };
return test;
}