After answering this question I was trying to find is_complete
template in Boost library and I realized that there is no such template in Boost.TypeTraits. Why
Solving this requires performing the computation in the default argument of the trait template, as attempting to change the definition of a template violates the ODR rule (although a combination of __COUNTER__
and namespace {}
can work around ODR).
This is written in C++11 but can be adjusted to work in C++03 mode of a moderately recent C++11-compatible compiler.
template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );
std::false_type is_complete_fn( ... );
template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};
Online demo.
The default argument is evaluated where the template is named, so it can contextually switch between different definitions. There is no need for a different specialization and definition at each use; you only need one for true
and one for false
.
The rule is given in §8.3.6/9, which applies equally to function default arguments and default template-arguments:
Default arguments are evaluated each time the function is called.
But beware, using this inside a template is almost sure to violate the ODR. A template instantiated on an incomplete type must not do anything differently from if it were instantiated on a complete type. I personally only want this for a static_assert
.
Incidentally, this principle may also be helpful if you want to go the other way and implement the functionality of __COUNTER__
using templates and overloading.