In template programming, static_assert
helps programmers to check constraint(s) on template arguments and generate human readable error messages on violation of constraint(s).
Consider this code,
template<typename T>
void f(T)
{
static_assert(T(), "first requirement failed to meet.");
static_assert(T::value, "second requirement failed to meet.");
T t = 10; //even this may generate error!
}
My thought is : if the first static_assert
fails, it means some requirement on T
doesn't meet, hence the compilation should stop, generating only the first error message — because it doesn't make much sense to continue the compilation just to generate more and more error messages, most of which often point to a single constraint violation. Hundreds of error messages, instead of just one, look very scary on the screen — I would even say, it defies the very purpose of static_assert
to some extent.
For example, if I call the above function template as:
f(std::false_type{});
GCC 4.8 generates the following:
main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:16:24: required from here
main.cpp:7:5: error: static assertion failed: first requirement failed to meet.
static_assert(T(), "first requirement failed to meet.");
^
main.cpp:9:5: error: static assertion failed: second requirement failed to meet.
static_assert(T::value, "second requirement failed to meet.");
^
main.cpp:11:11: error: conversion from 'int' to non-scalar type 'std::integral_constant<bool, false>' requested
T t = 10; //even this may generate error!
As you can see (online), that is too much of error. If the first static_assert
fails, it is very much likely that the rest of the code will also fail if compilation continues, then why continue compilation? In template programming, I'm sure many programmers don't want such cascading error messages!
I tried to solve this issue by splitting the function into multiple functions, in each checking only one constraint, as:
template<typename T>
void f_impl(T); //forward declaration
template<typename T>
void f(T)
{
static_assert(T(), "first requirement failed to meet.");
f_impl(T());
}
template<typename T>
void f_impl(T)
{
static_assert(T::value, "second requirement failed to meet.");
T t = 10;
}
f(std::false_type{}); //call
Now this generates this:
main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:24:24: required from here
main.cpp:10:5: error: static assertion failed: first requirement failed to meet.
static_assert(T(), "first requirement failed to meet.");
^
That is a lot of improvement — just one error message is a lot easier to read and understand (see online).
My question is,
- Why does the compilation not stop on the first
static_assert
? - Since splitting of function template and checking one constraint in each function_impl, helps only GCC and clang still generates lots of error, is there any way to improve diagnostics in a more consistent way — something which works for all compilers?
I agree with David Rodríguez - dribeas and in defense of compiler writers consider this example:
When the instantiation of
f<B>
starts the compiler (or the compiler writter) might think: "Humm... I need to instantiatecheck<U>
and people always complain that compiling templates is too slow. So I'll keep going and perhaps there's something wrong below and I don't event need to instantiatecheck
."I believe the reasoning above makes sense. (Notice that I'm not a compiler writter so I'm just speculating here).
Both GCC 4.8 and VS2010 keep compiling
f<B>
, postponing the instantiation ofcheck<B>
for later. Then they find the failing initialization and provide their own error messages. VS2010 stops immediately and I don't get my nice error message! GCC keeps going and yields the message that I wanted (but only after its own).Metaprogramming is tricky for the programmers and for the compilers.
static_assert
helps a lot but it's not a panacea.