C++11 static_assert and template instantiation
GCC is correct and the other compiler is correct too. Refer to 14.6p8 in the spec
If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.
Therefor, a compiler is free to reject the following
template<typename T>
void f() {
static_assert(0, "may trigger immediately!");
static_assert(sizeof(T) == 0, "may trigger immediately!");
}
If you want to go safe, you have to arrange it so the compiler cannot know until instantiation whether the boolean expression will be true or false. For example, get the value by getvalue<T>::value
, with getvalue
being a class template (one could specialize it, so the compiler cannot possibly know the boolean value already).
static_assert with partial template specialization
The expression in the static_assert
must be dependent on a template parameter if you wish it to be instantiation-time only. This is guaranteed by Standard- the implementation may (but has no obligation to) check static_assertion
s in templates that are not dependent on any template parameter.
Your code is a strange roundabout way of doing something like
template<typename T> struct S {
static_assert(std::is_integral<T>::value, "type unsupported");
void foo() {}
};
This clearly communicates to the compiler the dependency between the expression and the template parameter, and is far clearer and easier to read as well. I actually couldn't quite figure out if you wanted compilation to fail for integral types or for non-integral types.
When is static_assert evaluated relative to template instantiation
There ain't no such thing as the order of evaluation of static_assert
s relative to template instantiations. The standard simply says that, if the expression in the first parameter of static_assert
evaluates to false at compile time, the program is ill-formed and the diagnostic should include the text from the second parameter.
In other words, static_assert(false)
is treated the same as a syntax error or any other violation of well-formedness constraints. Naturally, if there are multiple errors that render a program ill-formed, the standard doesn't specify the order in which the compiler should issue diagnostic for them, nor even whether it should issue a message for each error.
What is the proper way to use static_assert in a non-instantiation template?
If the expression inside static_assert
depends on a template parameter, the assertion will be deferred until instantiation. You could, for example, define the following class template, deferred_false
:
#include <type_traits> // std::false_type
template<typename>
struct deferred_false: std::false_type {};
Then, use it in the static_assert
:
template <typename T, typename = void>
struct A {
void Print() {
static_assert(deferred_false<T>::value, "fall into unsupported Print function");
}
};
Default template is matching despite static_assert
Why is foo(int) in this code ambiguous?
Because the version with static_assert()
give error if selected but still exist; so the compiler doesn't know if choose the generic version or the integer enabled version.
Is there a better way to use templates to get this desired behavior (explicit bool specialization + implicit int specialization)?
A possible way is to avoid the generic version and SFINAE enable the version you need
The following is a full working example
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_same<T, bool>::value>::type foo(T val)
{ std::cout << "bool case " << val << std::endl; }
template <typename T>
typename std::enable_if< ! std::is_same<T, bool>::value
&& std::is_convertible<T, int>::value>::type foo(T val)
{ std::cout << "integer case " << (int)val << std::endl; }
int main()
{
foo(true); // bool case
foo(1); // integer case
foo(2U); // integer case
foo(3L); // integer case
foo(4UL); // integer case
foo(5LL); // integer case
foo(6ULL); // integer case
// foo((void*)nullptr); // compilation error
}
-- EDIT --
The OP
Sorry, I am still confused. Could you elaborate? I thought that due to SFINAE, that if an error occurred in substitution, it would use the other template.
Exactly.
The problem is when there isn't an error in substitution and the compiler have to choose between two different version of the same template.
I mean: in your example, when you call foo(5)
, there isn't error in substitution of
typename std::enable_if<!std::is_same<T,bool>::value
&& std::is_convertible<T,int>::value,int>::type=0>
So the compiler have to choose between the two template functions
template<typename T>
void foo(T val) {
static_assert(always_false<T>::value, "");
}
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
that differ only for a template value with a default value, so are (from the compiler point of view) indistinguishable.
And observe that
template<>
void foo<bool>(bool val) {
cout << "Is explicitly a bool! " << val << endl;
}
is a (full) template specialization but
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
isn't a template specialization (no partial template specialization of a function is admitted in C++11/14/17; you can partial specialize only structs/classes); is a generic template.
static_assert not evaluated in template parameter
Operand of decltype
is not evaluated and therefore does not require function definition to exist (or return type being complete) while function template is instantiated when the specialization is referenced in a context that requires a function definition to exist. So you need to instantiate function template for check inside of function body to take place:
template<typename T, auto Dummy = &has_foo_f<T>>
void bar(){}
static_assert and class templates
For B<A<1>> b;
, A<1>
is only used as template argument, which doesn't cause implicit instantiation of class template A
, then the static_assert
inside A
's definition won't be triggered.
When code refers to a template in context that requires a completely defined type, or when the completeness of the type affects the code, and this particular type has not been explicitly instantiated, implicit instantiation occurs. For example, when an object of this type is constructed, but not when a pointer to this type is constructed.
On the other hand, for A<1> a;
, A<1>
is required to be a complete type (to construct a
), then implicit instantiation happens, static_assert
is fired.
EDIT
You can use sizeof
(which requires the type to be complete) to cause the implicit instantiation and fire the static_assert
. e.g.
template <class T>
class B{
static_assert(sizeof(T) > 0, "static_assert for implicit instantiation");
};
C++11 static_assert: Parameterized error messages
You cannot do this. static_assert
wants a string literal. You have no way to assemble the semantic identity of T
and Y
into the string literal.
You can hope that the compiler gives an easy to read backtrace of the template instantiation stack and gives you the value of T
and Y
template parameters of the enclosing class template instantiation.
Other people thought about this too, see http://comments.gmane.org/gmane.comp.compilers.clang.devel/5073 for example.
Disallow a specific function template instantiation
I would use a static assert within your function call to create the proper failure during function instantiation:
template<typename T>
class is_double{ static const int value = false; }
template<>
class is_double<double>{ static const int value = true; }
template<typename T>
T convert( const char *argument ){
BOOST_STATIC_ASSERT( !is_double<T>::value );
//rest of code
}
And that should work within a function.
Related Topics
How to Get the Real and Total Length of Char * (Char Array)
Conversion Function for Error Checking Considered Good
How to Connect MySQL Database Using C++
How to Make This C++ Object Non-Copyable
How to Safely Use Openmp with C++11
Passing Integers as Constant References Versus Copying
How Much Footprint Does C++ Exception Handling Add
How to Construct a Std::String from a Std::Vector<Char>
C++ Compile Time Error: Expected Identifier Before Numeric Constant
Initialization: Parenthesis VS. Equals Sign
How to Make Visual Studio Use the Native Amd64 Toolchain
C++ Inheritance - Inaccessible Base
Getting "Source Type Is Not Polymorphic" When Trying to Use Dynamic_Cast
How to Create a Sparse Array in C++
How to Dynamically Allocate a Matrix