Is the following legal?
template<typename T>
std::enable_if_t<false, T> foo() {}
Usually, we have [temp.res]
The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template [...] and the template is not instantiated [...]
Clearly, no valid specialization can be generated for foo... unless you consider that std::enable_if could possibly be specialized such that type is present as void regardless of the first bool argument.
struct S {};
template<bool B>
struct std::enable_if<B, S> : std::type_identity<void> {};
Except... you can't specialize std::enable_if by [meta.rqmts]
Unless otherwise specified, the behavior of a program that adds specializations for any of the templates specified in [meta] is undefined.
To summarize, if you wrote a custom enable_if that mimics std::enable_if, the first snippet is legal. The question is whether the prohibition on specializing std::enable_if makes it illegal?
This post is inspired by this answer. The current form is adapted from an implementation of an independent library.
The question is whether the prohibition on specializing std::enable_if makes it illegal?
I would say that yes, it does. For starters, since such specialization would lead to undefined behavior, we can completely ignore the possibility of this ever happening. The contract is breached there, so discussing legality further is moot1.
Now, since we don't have to worry about specialization at all, we (as someone trying to determine the behavior of a program/implementation) can rely on to the described behavior in the standard. The description of std::enable_if specifies that it has no ::type member when its bool argument is false. So we are guaranteed std::enable_if<false, T>::type is always ill-formed.
This leads to inevitably meeting the criteria in [temp.res]/8. And now the program is ill-formed NDR.
1 - That is after all the very purpose of the [meta] clause you quote. It's to allow assuming things about the behavior of standard library components. Specifically, so that an implementation may optimize around said expected behavior.
Related
I think the following code is well-formed:
template< typename T >
using IsSigned = std::enable_if_t< std::is_signed_v< T > >;
template< typename T, IsSigned< T >... >
T myAbs( T val );
Others say that it is ill-formed, because §17.7 (8.3) of the C++17 standard:
Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if: (...) every valid specialization of a variadic template requires an empty template parameter pack, or (...)
In my opinion IsSigned< T >... is a dependent template parameter, therefore it can not be checked against §17.7 (8.3) in template definition time. IsSigned< T > could be for example void for one subset of Ts, int for another subset or substitution failure. For the void subset it is true, that the empty template parameter pack would be the only valid specialization, but the int subset could have many valid specializations. It depends on the actual T argument.
It means that the compiler must check it after the template instantiation, because T is not known before. At that point the full argument list is known, there is zero variadic arguments. The standard says the following (§17.6.3 (7)):
When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct
This is why I think it is well formed.
What do you think?
How can I track down this ambiguity for sure? It is hard to decide, because the code compiles but it means nothing: §17.7 (8.3) is NDR, the compilers do not have to raise any compilation error.
The code is ill-formed, no diagnostic is required.
If std::is_signed_v<T>, then std::enable_if_t<std::is_signed_v<T>> denotes the type void. Otherwise, std::enable_if_t<std::is_signed_v<T>> does not denote a valid type. Therefore, every valid specialization of myAbs requires an empty template parameter pack.
Per [meta.rqmts]/4, the program has undefined behavior if std::enable_if is specialized. Therefore, the aforementioned behavior cannot be changed.
In my opinion IsSigned< T >... is a dependent template parameter,
therefore it can not be checked against §17.7 (8.3) in template
definition time. IsSigned< T > could be for example void for one
subset of Ts, int for another subset or substitution failure. For
the void subset it is true, that the empty template parameter pack
would be the only valid specialization, but the int subset could
have many valid specializations. It depends on the actual T
argument.
The compiler cannot check it, in the same way it cannot, say, solve an arbitrary equation for you. NDR (no diagnostic required) is made exactly for such cases — the program is ill-formed and would require a diagnostic if the compiler is actually capable of detecting that. NDR permits the compiler not to check it.
When N is zero, the instantiation of the expansion produces an empty
list. Such an instantiation does not alter the syntactic
interpretation of the enclosing construct.
The rule we are talking about is a semantic rule, not a syntactic rule, because syntactic rules are in [gram].
So what is the rationale for the NDR rules? In general, they address problems that are not reproducible among implementation strategies. For example, they may cause the code to misbehave in some implementation strategies, but do not cause any problems (and cannot be easily) in others.
Also, note that the standard talks in terms of program with terms like "ill-formed". Therefore, it is not always plausible to talk about the well-formed ness of an isolated code snippet. In this case, std::enable_if is required not to be specialized, but the situation may get more complicated otherwise.
template <int answer> struct Hitchhiker {
static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
While trying to disable general template instantiation with static_assert I discovered that the above code in clang generates the assert error even when the template is not instantiated, while gcc generates the assert error only when instantiating Hitchhiker with a parameter other than 42.
Fiddling around I found that this assert:
template <int answer> struct Hitchhiker {
static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
behaves the same on both compilers: the assert kicks in only when the general template is instantiated.
What does the standard says, which compiler is right?
g++ 4.9.2
clang++ 3.50
Both compilers are correct. From [temp.res]/8:
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic
required.
There does not exist a valid specialization that can be generated from the primary template Hitchhiker, so it is ill-formed, no diagnostic required. clang chooses to issue a diagnostic anyway.
If you only want to allow 42, then simply don't define the general template:
template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};
Quotes found by #TartainLlama
If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required.
N4296 [temp.res]/8
This applies immediately after the primary template is defined (the one with the static_assert in it). So the later specialization (for 42) cannot be considered, as it does not exist yet.
The next question is if static_assert( sizeof(answer) != sizeof(answer), depends on answer. Semantically it does not, syntactically it does, and standard-wise:
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
N4296 [temp.dep]/1
The construct sizeof(answer) != sizeof(answer) does not differ from one instantiation to another. So such a construct does not depend on the template parameters. Which means the entire static_assert does not depend on the template parameter.
Thus your program is ill formed, no diagnostic required. Issuing an arbitrary diagnostic (such as the static_assert failing) is valid compiler behavior. Missing the problem is valid compiler behavior. The behavior of a program compiled from an ill formed, no diagnostic required program is not defined by the standard: it is undefined behavior. Nasal demons are permitted.
Fancy attempts (like sizeof(int[answer])!=sizeof(int[answer]) may please the current god compiler, but does not make your program more well formed.
You could make a case where the compiler is unlikely to be able to catch you at it, but the ill-formed-ness remains regardless of the ability for the compiler to catch you with it. As a general rule, C++ wants to leave itself (and its compilers) freedom to find invalid template code "earlier than instantiation"; this means that template code must produce possibly legal code.
It is possible you want something like =delete with a message attached.
C++11
14.8.2 - Template Argument Deduction - [temp.deduct]
7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (ie. non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions.
C++14
14.8.2 - Template Argument Deduction - [temp.deduct]
7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (ie. non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered.
The added sentence explicitly states the order of substitution when dealing with template parameters in C++14.
The order of substitution is something that most often isn't given a lot of attention. I have yet to find a single paper on why this matters. Maybe this is because C++1y hasn't been fully standardized yet, but I'm assuming such a change must have been introduced for a reason.
The question:
Why, and when, does the order of template argument substitution matter?
As stated C++14 explicitly says that the order of template argument substitution is well-defined; more specifically it will be guaranteed to proceed in "lexical order and halt whenever a substitution causes the deduction to fail.
Compared to C++11 it will be much easier to write SFINAE-code that consists of one rule depending on another in C++14, we will also move away from cases where undefined ordering of template substitution can make our entire application suffer from undefined-behaviour.
Note: It's important to note that the behavior described in C++14 has always been the intended behavior, even in C++11, just that it hasn't been worded in such an explicit way.
What is the rationale behind such change?
The original reason behind this change can be found in a defect report originally submitted by Daniel Krügler:
C++ Standard Core Language Defect Reports and Accepted Issues, Revision 88
1227. Mixing immediate and non-immediate contexts in deduction failure
FURTHER EXPLANATION
When writing SFINAE we as developers depend on the compiler to find any substitution that would yield an invalid type or expression in our template when used. If such invalid entity is found we'd like to disregard whatever the template is declaring and move on to hopefully find a suitable match.
Substitution Failure Is Not An Error, but a mere.. "aw, this didn't work.. please move on".
The problem is that potential invalid types and expressions are only looked for in the immediate context of the substitution.
14.8.2 - Template Argument Deduction - [temp.deduct]
8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments.
[ Note: Access checking is done as part of the substitution process. --end note ]
Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
[ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. --end note]
In other words a substitution that occurs in a non-immediate context will still render the program ill-formed, which is why the order of template substitutions is important; it can change the whole meaning of a certain template.
More specifically it can be the difference between having a template which is usable in SFINAE, and a template which isn't.
SILLY EXAMPLE
template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };
template<
class T,
class = typename T::type, // (E)
class U = typename inner_type<T>::type // (F)
> void foo (int); // preferred
template<class> void foo (...); // fallback
struct A { };
struct B { using type = A; };
int main () {
foo<A> (0); // (G), should call "fallback "
foo<B> (0); // (H), should call "preferred"
}
On the line marked (G) we want the compiler to first check (E) and if that succeeds evaluate (F), but before the standard change discussed in this post there was no such guarantee.
The immediate context of the substitutions in foo(int) includes;
(E) making sure that the passed in T has ::type
(F) making sure that inner_type<T> has ::type
If (F) is evaluated even though (E) results in an invalid substitution, or if (F) is evaluated before (E) our short (silly) example won't make use of SFINAE and we will get an diagnostic saying that our application is ill-formed.. even though we intended for foo(...) to be used in such case.
Note: Notice that SomeType::type is not in the immediate context of the template; a failure in the typedef inside inner_type will render the application ill-formed and prevent the template from making use of SFINAE.
What implications will this have on code development in C++14?
The change will dramatically ease the life of language-lawyers trying to implement something which is guaranteed to be evaluated in a certain way (and order), no matter what conforming compiler they are using.
It will also make template argument substitution behave in a more natural way to non-language-lawyers; having the substitution occur from left-to-right is far more intuitive than erhm-like-any-way-the-compiler-wanna-do-it-like-erhm-....
Isn't there any negative implication?
The only thing I can think of is that since the order of substitution will occur from left-to-right a compiler is not permitted to handle multiple substitutions at once using an asynchronous implementation.
I have yet to stumble across such implementation, and I doubt that it would result in any major performance gain, but at least the thought (in theory) kinda fits on the "negative" side of things.
As an example: A compiler will not be able to use two threads that simultaneously does substitutions when instantating a certain template without any mechanism to act like the substitutions that occured after a certain point never happened, if that is required.
The story
Note: An example that could have been taken from real life will be presented in this section to describe when and why the order of template argument substitution matters. Please let me know (using the comment section) if anything is not clear enough, or maybe even wrong.
Imagine that we are working with enumerators and that we'd like a way to easily obtain the underlying value of the specified enumeration.
Basically we are sick and tired of always having to write (A), when we would ideally want something closer to (B).
auto value = static_cast<std::underlying_type<EnumType>::type> (SOME_ENUM_VALUE); // (A)
auto value = underlying_value (SOME_ENUM_VALUE); // (B)
THE ORIGINAL IMPLEMENTATION
Said and done, we decide to write an implementation of underlying_value looking as the below.
template<class T, class U = typename std::underlying_type<T>::type>
U underlying_value (T enum_value) { return static_cast<U> (enum_value); }
This will ease our pain, and seems to do exactly what we want; we pass in an enumerator, and get the underlying value back.
We tell ourselves that this implementation is awesome and ask a colleague of ours (Don Quixote) to sit down and review our implementation before pushing it out into production.
THE CODE REVIEW
Don Quixote is an experienced C++ developer that has a cup of coffee in one hand, and the C++ standard in the other. It's a mystery how he manages to write a single line of code with both hands busy, but that's a different story.
He reviews our code and comes to the conclusion that the implementation is unsafe, we need to guard std::underlying_type from undefined-behaviour since we can pass in a T which is not of enumeration type.
20.10.7.6 - Other Transformations - [meta.trans.other]
template<class T> struct underlying_type;
Condition: T shall be an enumeration type (7.2)
Comments: The member typedef type shall name the underlying type of T.
Note: The standard specifies a condition for underlying_type, but it doesn't go any further to specifiy what will happen if it's instantiated with a non-enum. Since we don't know what will happen in such case the usage falls under undefined-behavior; it could be pure UB, make the application ill-formed, or order edible underwear online.
THE KNIGHT IN SHINING ARMOUR
Don yells something about how we always should honor the C++ standard, and that we should feel tremendous shame for what we have done.. it's unacceptable.
After he has calmed down, and had a few more sips of coffee, he suggests that we change the implementation to add protection against instantiating std::underlying_type with something which isn't allowed.
template<
typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type, // (C)
typename U = typename std::underlying_type<T>::type // (D)
>
U underlying_value (T value) { return static_cast<U> (value); }
THE WINDMILL
We thank Don for his discoveries and are now satisfied with our implementation, but only until we realize that the order of template argument substitution isn't well-defined in C++11 (nor is it stated when the substitution will stop).
Compiled as C++11 our implementation can still cause an instantiation of std::underlying_type with a T that isn't of enumeration type because of two reasons:
The compiler is free to evaluate (D) before (C) since the substitution order isn't well-defined, and;
even if the compiler evaluates (C) before (D), it's not guaranteed that it won't evaluate (D), C++11 doesn't have a clause explicitly saying when the substitution chain must stop.
The implementation by Don will be free from undefined-behavior in C++14, but only because C++14 explicitly states that the substitution will proceed in lexical order, and that it will halt whenever a substitution causes deduction to fail.
Don might not be fighting windmills on this one, but he surely missed a very important dragon in the C++11 standard.
A valid implementation in C++11 would need to make sure that no matter the order in which the substitution of template parameters occur the instantation of std::underlying_type won't be with an invalid type.
#include <type_traits>
namespace impl {
template<bool B, typename T>
struct underlying_type { };
template<typename T>
struct underlying_type<true, T>
: std::underlying_type<T>
{ };
}
template<typename T>
struct underlying_type_if_enum
: impl::underlying_type<std::is_enum<T>::value, T>
{ };
template<typename T, typename U = typename underlying_type_if_enum<T>::type>
U get_underlying_value (T value) {
return static_cast<U> (value);
}
Note: underlying_type was used because it's a simple way to use something in the standard against what is in the standard; the important bit is that instantiating it with a non-enum is undefined behavior.
The defect-report previously linked in this post uses a much more complex example which assumes extensive knowledge about the matter. I hope this story is a more suitable explanation for those who are not well read up on the subject.
static_assert seems to be a very nice feature together with templates.
However, I have trouble finding functions in the standard library for doing various tests at compile time.
For example, I am looking for a function to check whether a type is a subtype of another one. boost::is_base_of does the job, however, is a comparable function in std, so I do not need to rely on boost.
Basically, is there a good source for a list of functions which can be used in static_assert and are contained in the standard library of C++11?
When is static_assert executed? Can I put it anywhere in a template and it is evaluated for each template instanciation? Could it be used to constrain template parameters to be a specific subtype of a class?
Take a look at the final C++11 draft, section 20.7, particularly the <type_traits> header.
What you are asking is: std::is_base_of<base, derived>::value;
Regarding your question: static_assert can be evaluated whenever the compiler sees fit, but it will usually:
In a template: if the expression uses dependent names, in instatiation time; else, in definition time.
Out of template: in definition time.
In addition to #rodrigo’s answer (he was faster …),
When is static assert executed? Can I put it anywhere in a template and it is evaluated for each template instanciation? Could it be used to constrain template parameters to be a specific subtype of a class?
Unfortunately, no. For instance, a static_assert(false, "bummer"); is always executed, no matter the template. This in particular fails if you want to (partially) specialise a template.
The standard (§7.4) says:
[If the condition to static_assert is false] the program is ill-formed, and the resulting diagnostic message (1.4) shall include the text of the string-literal, […]
Which is unfortunately quite unspecific but this lack of specificity is in fact exactly how static_assert behaves when it’s not dependent on a template type.
You need to make the condition in a static_assert depend on the template argument to bind its execution to the particular template argument.
So the following would fail:
template <typename T>
struct some_type {
static_assert(false, "T must be a pointer type");
};
template <typename T>
struct some_type<T*> {
// …
};
Finally, I heartily recommend you read Marthino’s article on More type traits which details this process more, and gives hints on how to solve many trait-related problems elegantly.
From the book - C++ Templates: The Complete Guide by David, Nicolai
Thus, templates are compiled twice:
Without instantiation, the template code itself is checked for correct syntax. Syntax errors are discovered, such as missing
semicolons.
At the time of instantiation, the template code is checked to ensure that all calls are valid. Invalid calls are discovered, such as
unsupported function calls.
Keeping the first point, I wrote -
template<typename T>
void foo( T x)
{
some illegal text
}
int main()
{
return 0;
}
It build fine on Visual Studio 2010 with out any warnings with optimizations turned off. How ever, it failed on gcc-4.3.4. Which one is complying to the C++ standard ? Is it mandatory for template code to get compiled even with out template instantiation ?
The program in question is ill-formed, but the C++ standard does not require a diagnostic in this case, so both Visual Studio and GCC are behaving in a compliant fashion. From §14.6/7 of the C++03 standard (emphasis mine):
Knowing which names are type names allows the syntax of every template definition to be checked. No
diagnostic shall be issued for a template definition for which a valid specialization can be generated. 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. If a type used in a non-dependent name is incomplete
at the point at which a template is defined but is complete at the point at which an instantiation is done, and
if the completeness of that type affects whether or not the program is well-formed or affects the semantics
of the program, the program is ill-formed; no diagnostic is required. [Note: if a template is instantiated,
errors will be diagnosed according to the other rules in this Standard. Exactly when these errors are diagnosed
is a quality of implementation issue. ] [Example:
int j;
template<class T> class X {
// ...
void f(T t, int i, char* p)
{
t = i; // diagnosed if X::f is instantiated
// and the assignment to t is an error
p = i; // may be diagnosed even if X::f is
// not instantiated
p = j; // may be diagnosed even if X::f is
// not instantiated
}
void g(T t) {
+; //may be diagnosed even if X::g is
// not instantiated
}
};
—end example]
The book you're looking at seems to reflect (mostly) that author's observations about how compilers really work, not the requirement(s) of the standard. The standard doesn't really say much to give extra leniency to a compiler about ill-formed code inside a template, just because it's not instantiated.
At the same time, the book is correct that there are some things a compiler really can't do much about checking until it's instantiated. For example, you might use a dependent name as the name of a function (or invoke it like a function, anyway -- if it's functor instead that would be fine too). If you instantiate that template over a class where it is a function, fine and well. If you instantiate it over a class where it's really an int, attempting to call it will undoubtedly fail. Until you've instantiated it, the compiler can't tell which is which though.
This is a large part of what concepts were really intended to add to C++. You could directly specify (for example) that template X will invoke T::y like a function. Then the compiler could compare the content of the template with the declarations in the concept, and determine whether the body of the template fit with the declaration in the concept or not. In the other direction, the compiler only needed to compare a class (or whatever) to the concept to determine whether instantiating that template would work. If it wasn't going to work, it could report the error directly as a violation of the relevant concept (as it is now, it tries to instantiate the template, and you frequently get some strange error message that indicates the real problem poorly, if at all).