Why does the order of template argument substitution matter? - c++

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.

Related

C++20: Validate Template Bodies Against Concepts

C++20 introduces concepts, which allows us to specify in the declaration of a template that the template parameters must provide certain capabilities. If a template is instantiated with a type that does not satisfy the constraints, compilation will fail at instantiation instead of while compiling the template's body and noticing an invalid expression after substitution.
This is great, but it begs the question: is there a way to have the compiler look at the template body, before instantiation (i.e. looking at it as a template and not a particular instantiation of a template), and check that all the expressions involving template parameters are guaranteed by the constraints to exist?
Example:
template<typename T>
concept Fooer = requires(T t)
{
{ t.foo() };
};
template<Fooer F>
void callFoo(F&& fooer)
{
fooer.foo();
}
The concept prevents me from instantiating callFoo with a type that doesn't support the expression that's inside the template body. However, if I change the function to this:
template<Fooer F>
void callFoo(F&& fooer)
{
fooer.foo();
fooer.bar();
}
This will fail if I instantiate callFoo with a type that defines foo (and therefore satisfies the constraints) but not bar. In principal, the concept should enable the compiler to look at this template and reject it before instantiation because it includes the expression fooer.bar(), which is not guaranteed by the constraint to exist.
I assume there's probably backward compatibility issues with doing this, although if this validation is only done with parameters that are constrained (not just typename/class/etc. parameters), it should only affect new code.
This could be very useful because the resulting errors could be used to guide the design of constraints. Write the template implementation, compile (with no instantiations yet), then on each error, add whatever requirement is needed to the constraint. Or, in the opposite direction, when hitting an error, adjust the implementation to use only what the constraints provide.
Do any compilers support an option to enable this type of validation, or is there a plan to add this at any point? Is it part of the specification for concepts to do this validation, now or in the future?
Do any compilers support an option to enable this type of validation, or is there a plan to add this at any point? Is it part of the specification for concepts to do this validation, now or in the future?
No, no, and no.
The feature you're looking for is called definition checking. That is, the compiler checks the definition of the template at the point of its definition based on the provided concepts, and issues errors if anything doesn't validate. This is how, for instance, Rust Traits, Swift Protocols, and Haskell Typeclasses work.
But C++ concepts don't work like that, and it seems completely infeasible to ever add support for such a thing given that C++ concepts can be arbitrary expressions rather than function signatures (as they are in other languages).
The best you can do is thoroughly unit test your templates with aggressively exotic types that meet your requirements as minimally as possible (the term here is archetype) and hope for the best.
TL;DR: no.
The design for the original C++11 concepts included validation. But when that was abandoned, the new version was designed to be much more narrow in scope. The new design was originally built on constexpr boolean conditions. The eventual requires expression was added to make these boolean checks easier to write and to bring some sanity to relationships between concepts.
But the fundamentals of the design of C++20 concepts makes it basically impossible to do full validation. Even if a concept is built entirely out of atomic requires expressions, there isn't a way to really tell if an expression is being used exactly in the code the way it is in the requires expression.
For example, consider this concept:
template<typename T, typename U>
concept func_to_u = requires(T const t)
{
{t.func()} -> std::convertible_to<U>;
};
Now, let's imagine the following template:
template<typename T, typename U> requires func_to_u<T, U>
void foo(T const &t)
{
std::optional<U> u(std::in_place, t.func());
}
If you look at std::optional, you find that the in_place_t constructor doesn't take a U. So... is this a legitimate use of that concept? After all, the concept says that code guarded by this concept will call func() and will convert the result to a U. But this template does not do this.
It instead takes the return type, instantiates a template that is not guarded by func_to_u, and that template does whatever it wants. Now, it turns out that this template does perform a conversion operation to U.
So on the one hand, it's clear that our code does conform to the intent of func_to_u. But that is only because it happened to pass the result to some other function that conformed to the func_to_u concept. But that template had no idea it was subject to the limitations of convertible_to<U>.
So... how is the compiler supposed to detect whether this is OK? The trigger condition for failure would be somewhere in optional's constructor. But that constructor is not subject to the concept; it's our outer code that is subject to the concept. So the compiler would basically have to unwind every template your code uses and apply the concept to it. Only it wouldn't even be applying the whole concept; it would just be applying the convertible_to<U> part.
The complexity of doing that quickly spirals out of control.

Is SFINAE on unspecializable std templates ill-formed?

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.

Dependent non-type parameter packs: what does the standard say?

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.

Is there any guarantee on the order of substitution in a function template after type deduction?

Consider this function template:
template<typename T>
typename soft_error<T>::type foo(T, typename hard_error<T>::type)
{ }
After deducing type T from the type of the first argument in the call to foo(), the compiler will proceed to substitute T and instantiate the function signature.
If substitution for the return type gets executed first, causing a simple substitution failure, the compiler will discard this function template when computing the overload set and search for other viable overloads (SFINAE).
On the other hand, if substitution for the second function parameter occurs first, causing a hard error (e.g. because of a substitution failure in a non-immediate context), the entire compilation would fail.
QUESTION: Is there any guarantee on the order in which substitution will be performed for the function parameters and return types?
NOTE: This example seems to show that on all major compilers (VC11 was tested separately and gave identical results) substitution for the return type occurs before substitution for the parameter types.
[NOTE: This was not originally meant to be a self-answered question, but I happened to find out the solution while crafting the question]
Is there any guarantee on the order in which substitution will be performed for the function parameters and return types?
Not in the current standard.
However, this Defect Report (courtesy of Xeo) shows that this is indeed intended to be the case. Here is the proposed new wording for Paragraph 14.8.2/7 of the C++11 Standard (which has become part of the n3485 draft):
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 (i.e., 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. [...]
As correctly pointed out by Nicol Bolas in the comments to the question, lexical order means that a trailing return type would be substituted after the parameter types, as shown in this live example.

C++11 static_assert (and functions to be used therein)

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.