There are cases where one uses an always_false helper to e.g. cause unconditional static_assert failure if instantiation of some template is attempted:
template <class... T> struct always_false : std::false_type {};
template<class T>
struct UsingThisShouldBeAnError {
static_assert(always_false<T>::value, "You should not use this!");
};
This helper is necessary because a template definition must (at least theoretically) have at least one set of template parameters for which a valid specialization can be produced in order for the program to be well-formed:
[temp.res]/8: The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template [...] and the template is not instantiated, or
[...]
(Writing static_assert(false, "You should not use this!"); above would thus be ill-formed and a compiler could always fire the static assert, even without the template being instantiated, which is not the intention.)
Here is a quick sampling of questions involving this pattern (including further explanation):
Forbids functions with `static_assert`
Are static_asserts to be evaluated if a member template isn't instantiated?
Conditional compilation of templates
It might be useful to have always_false as a tool in the standard library so we don't have to constantly write it again. However, the answer to the following question makes me wonder whether this is even possible:
Dependent non-type parameter packs: what does the standard say?
There the argument is made (also with respect to [temp.res]/8) that std::enable_if_t<T> is always either void or not a type and that it is illegal for anyone to specialize it further. Therefore, a template that relies on the theoretical "specializability" of std::enable_if to avoid the [temp.res]/8 clause actually causes the program to be ill-formed, no diagnostic required.
Coming back to my question: If the standard provided always_false, it would have to forbid library users from specializing it as usual (for obvious reasons). But by the above reasoning, that would defeat the whole point of always_false (namely that it could theoretically be specialized to something other than std::false_type) - with respect to [temp.res]/8 it would be the same as using std::false_type directly.
Am I wrong in this reasoning? Or is it actually impossible for the standard library to provide always_false in a meaningful/useful way (without core language changes)?
In C++20, with lambda, you might do something like:
template <class... T> struct always_false : std::false_type {};
// To have true, but for a type that user code can't reuse as lambda types are unique.
template <> struct always_false<decltype([](){})> : std::true_type{};
After reflection, I think it is not possible: enclosing template might have other restrictions that the hypothetical type(s) for the specialization should fulfill:
With static_assert(is_enum_v<T> && always_false_v<T>), that type should be an enum.
And even more constrained, with static_assert(is_same_v<T, int> && always_false_v<T>), it is for int.
To paraphrase Jarod's idea, It could be something like
template <class... T> struct always_false : std::false_type {};
template <> struct always_false</* implementation defined */> : std::true_type{};
Where /* implementation defined */ can be filled by std::_ReservedIdentifer. User code can't access it, since the identifier is reserved to the library, but there exists a specialization that is true. That should avoid questions about the ODR and lambdas in specializations.
All such attempts result in a program that is ill-formed, no diagnostic required.
The clauses that block you from using static_assert(false) make your program ill-formed, no diagnostic required based on the actual possibility of making an actual instantiation, not based on if the compiler can work it out.
These tricks simply make it harder for the compiler to detect the fact your program is ill-formed. The diagnostic they issue is not required, and your ability to bypass the diagnostic being issued just means you made the compiler produce an ill-formed program for which the standard places no restrictions on its behavior.
(Writing static_assert(false, "You should not use this!"); above would thus be ill-formed and a compiler could always fire the static assert, even without the template being instantiated, which is not the intention.)
The exact same conclusion holds for your
template <class... T> struct always_false : std::false_type {};
template<class T>
struct UsingThisShouldBeAnError {
static_assert(always_false<T>::value, "You should not use this!");
};
I claim there is no valid instantiation of UsingThisShouldBeAnError in the above program.
http://eel.is/c++draft/temp.res#6.1
The program is ill-formed, no diagnostic required, if:
(6.1) no valid specialization can be generated for a template [...]"
No valid specialization can be generated for this template.
To avoid this trap, your program must have
template <> struct always_false<SomeListOfTypes> : std::true_type {};
and if an always_false is specified in the standard for which this cannot occur, using always_false from the standard doesn't help you at all.
because the standard requires that the specialization "can be generated".
If the only way to instantiate your template is within an ill-formed program, then that is a huge stretch on the word "can". So using reserved or magic types you aren't allowed to use within the true_type specialization is not really plausible.
Stepping back, the purpose of the "ill-formed, ndr" there is because the standard writers want to permit diagnostics of broken code, but don't want to mandate it. Detecting such broken code in general is a halt-hard problem, but simple cases can be detected. And optimizing around the assumption that the code isn't broken can be useful.
All attempts to inject static_assert(false, "message") are working around the intention that C++ code is intended to be valid.
We have a construct for a function for whom successful lookup is an error already in the language.
template<class T>
void should_never_be_found(tag<T>) = delete;
the hole, of course, is the lack of a diagnostic message here.
template<class T>
void should_never_be_found(tag<T>) = delete("Provide an custom ADL overload!");
also, the inability to =delete template specializations.
What you appear to want is:
template<class T>
struct UsingThisShouldBeAnError = delete("You should not use this");
which is direct, intentional, and consistent with how other =delete cases work.
The static_assert bypass requires magic and "tricking" the compiler to bypass parts of the standard explicitly written to prevent you from doing what you want to do.
This =delete("string") and =delete syntax on template classes is missing from the language.
just use option -fdelayed-template-parsing
Related
The standard says in [temp.res]/8:
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. ... [ 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. — end note ]
My question is: Does the following count as a valid specialization that can be generated?
#include <type_traits>
template <typename T, typename Enable = void>
class A;
template <typename T>
class A<T, typename std::enable_if<not std::is_same<T, int>::value>::type>
{
public:
static_assert(std::is_same<T, int>::value, "should not be here!");
};
On the one hand, the static_assert essentially amounts to a static_assert(false, "should not be here"); (we can't both not be an int and be an int at the same time), which isn't allowed. On the other hand, as far as SFINAE is concerned, the type, e.g., A<double> is perfectly well formed and can be generated, but will immediately throw a static assertion failure. This currently works in GCC 8.3, but given the "ill-formed, no diagnostic required" part of the standards quote above, I want to make sure that it actually should work. (Note: work here is defined to mean it throws a static assertion failed if I try to instantiate a A<double> and compiles silently if I don't)
No, it's not meant to work. That is not a valid C++ program.
Per the very paragraph you cite, the template definition of your partial specialization is ill-formed NDR. There is no valid specialization that may be generated from it.
The standard typically designates something as ill-formed NDR because checking it reliably in the general case would amount to contradicting Rice's theorem. So compilers aren't obligated to try, but they may try some heuristic analysis or other.
GCC and Clang often diagnose more as heuristics are added to them. I wouldn't try to claim "this is dependent" as a shield against that. The code itself is invalid from the get-go.
I read a couple of answers yesterday, on the use of static_assert(false, "Some message") inside the else clause of an if constexpr. I understand that it's considered to be ill-formed, according to the standard (even if some compilers, including MSVC2017, will accept it). Qt will also mark this as an error.
My question is, is the code below well-formed according to the standard? (I'm inclined to think so, but I'd like a confirmation.)
template <typename TypeOfValue>
static void PushValue(duk_context* ctx, TypeOfValue value) {
// Push value onto duktape stack
if constexpr (std::is_same<TypeOfValue, int>::value) {
// Push int
duk_push_int(ctx, value);
} else if constexpr (std::is_same<TypeOfValue, uint32_t>::value) {
// Push uint
duk_push_uint(ctx, value);
} else {
// Unsupported type
static_assert(bool_value<false, TypeOfValue>(), "Unsupported type");
}
}
template <bool value, typename T>
static constexpr bool bool_value() {return value;}
Edit:
It seems, from the comment I got, that bool_value should instead be defined like this:
template<bool value, typename T>
struct bool_value {
static constexpr bool value = value;
};
with the usage pattern
// Unsupported type
static_assert(bool_value<false, TypeOfValue>::value, "Unsupported type");
It's then well-formed, only because it's possible for bool_value to be specialized into a version which returns true for the expression bool_value<false, TypeOfValue>::value.
Both of your attempts (with the function and with the struct) are well-formed as is.
The other answer mentions [temp.res]/8, but I disagree with how it was interpreted.
The validity of a template may be checked prior to any instantiation. ... The program is ill-formed, no diagnostic required, if:
— no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or ...
Both the function and struct you wrote can be specialized to be true. I believe the mere possibility of specialization is enough, you don't actually need to add a dummy true specialization to make the code well-formed.
According to my understanding (and according to common sense, I hope), the point of this part of the standard is to allow compilers to check validity of templates and if constexpr branches early (when they are seen for the first time), and reject the ones that can't possibly be instantiated.
Every branch in your template can potentially be instantiated, because bool_value() can be specialized later. I doubt a sane compiler is going to reject your code due to bool_value() not being specialized yet.
The accepted answer of the question you linked explains why it's ill-formed. Note the quote from the Standard temp.res-8, which says (emphasis mine)
The validity of a template may be checked prior to any instantiation. [...] The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or...
[...]
Otherwise, no diagnostic shall be issued for a template for which a valid specialization can be generated.
[ Note: If a template is instantiated, errors will be diagnosed according to the other rules in this document.
Exactly when these errors are diagnosed is a quality of implementation issue.
— end note
]
In the cppreference page about if you can see the following workaround (emphasis mine)
The common workaround for such a catch-all statement is a type-dependent expression that is always false:
template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
if constexpr (std::is_arithmetic_v<T>)
// ...
else
static_assert(dependent_false<T>::value, "Must be arithmetic"); // ok
}
As noted in the comments by Evg
theoretically, this is still ill-formed, because [...] is always false unless you actually have a specialization that returns true.
So you could add that specialization to the previous snippet
namespace {
// Do not unleash.
struct aggressive_unicorn_type;
}
template<> struct dependent_false<aggressive_unicorn_type> : std::true_type {};
Whether this is necessary to satisfy the Standard document to the letter or not it depends on the interpretation of "can be generated".
I wonder if it really matters too. The OP is asking if the code is well-formed, but they are actually trying to produce a code which is ill-formed, with a diagnostic (that's what static_assert(false) implies, see dcl.pre) under certain conditions.
As the asker already noted, there are other methods to force a compiler error when arguments of the "wrong" type are passed (consider concepts, of the upcoming standard) and, maybe, if we want to use static_asserts in conjunction with if constexpr the best option may be a type-dependent expression that is not always false.
#include <type_traits>
void f(int) {};
void g(unsigned) {};
template< class T>
constexpr inline bool is_supported =
std::is_same_v<T, int> ||
std::is_same_v<T, unsigned> ||
std::is_same_v<T, char>;
template <class T>
void use(T value) {
static_assert(is_supported<T>, "Not supported");
if constexpr (std::is_same<T, int>::value) {
f(value);
} else if constexpr (std::is_same_v<T, unsigned>){
g(value);
} else {
static_assert( !is_supported<T>, "Not yet implemented");
}
}
I'll just add my own final thoughts as an answer, since I seem to not be getting a quite principled enough answer from other sources.
My conclusion after reading a couple of other answers is this:
The compiler is allowed, according to the standard, to evaluate static_assert(false, "...") early. If it does, it produces a program that can never compile.
A program that can never compile is ill-formed.
So, the problem is that the compiler is allowed to evaluate static_assert early, even within an if constexpr.
Forcing it to delay the evaluation until instantiation time, by introducing a false that artificially depends upon a template parameter, seems a bit contrived to me. But I accept that there are other considerations which probably makes it the correct language choice anyway. (That's the sort of thing I was hoping for an expert answer to shed some light upon.)
Assume that we want to make a template class that can only be instantiated with numbers and should not compile otherwise. My attempt:
#include <type_traits>
template<typename T, typename = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");
//OnlyNumbers<C>* ptr;
};
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};
struct Foo{};
int main()
{
OnlyNumbers<int>{}; //Compiles
//OnlyNumbers<Foo>{}; //Error
}
Live demo - All three major compilers seem to work as expected. I'm aware that there is already a similar question with answers quoting the standard. The accepted answer uses temp.res.8 together with temp.dep.1 to answer that question. I think my question is a bit different because I'm asking precisely about my example and I'm not sure about the standard's opinion on it.
I would argue that my program is not ill-formed and that it should fail to compile if and only if the compiler tries to instantiate the base template.
My reasoning:
[temp.dep.1]:
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
This should make std::is_same<C,T>::value dependent on T.
[temp.res.8.1]:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
Does not apply because there exist a valid specialization, in particular OnlyNumbers<C> is valid and can be used inside the class for e.g. defining a member pointer variable(ptr). Indeed by removing the assert and uncommenting the ptr, OnlyNumbers<Foo> lines the code compiles.
[temp.res.8.2 - 8.4] does not apply.
[temp.res.8.5] I don't think this applies either but I cannot say that I fully understand this section.
My question is: Is my reasoning correct? Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
*Primarily I'm interested in class templates, feel free to include function templates. But I think the rules are the same.
**That means that there is no T which can be used to instantiate the template from the outside like T=C could be used from the inside. Even if C could be accessed somehow I don't think there's a way to refer to it because it leads to this recursion OnlyNumbers<OnlyNumbers<...>::C>.
EDIT:
Just to clarify, I know that I can construct an expression that will be false exactly if none of the other specializations match. But that can become wordy quite quickly and is error-prone if specializations change.
Static assertions are there to be used directly in the class without doing anything complicated.
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
// ....
};
In some cases, you might get additional error messages since instanciating OnlyNumbers for non-arithmetic types might cause more compilation errors.
One trick I have used from time to time is
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
// ....
};
In this case, your class gets instanciated with int, a valid type. Since the static assertion fails anyway, this does not have negative effects.
Well... I don't understand what do you mean with
[[temp.res.8.1]] Does not apply because there exist a valid specialization, in particular OnlyNumbers is valid and can be used inside the class for e.g. defining a member pointer variable(ptr).
Can you give an example of OnlyNumers valid and compiling main template based on OnlyNumbers<C>?
Anyway, it seems to me that the point is exactly this.
If you ask
Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
it seems to me that (maybe excluding a test that is true only when another specialization matches) the answer is "no" because of [temp.res.8.1].
Maybe you could let a little open door open to permit an instantiation but available only is someone really (really!) want instantiate it.
By example, you could add a third template parameter, with different default value, and something as follows
template<typename T, typename U = void, typename V = int>
struct OnlyNumbers
{
static_assert(std::is_same<T, U>::value, "test 1");
static_assert(std::is_same<T, V>::value, "test 2");
};
This way you open a door to a legit instantiation
OnlyNumbers<Foo, Foo, Foo> o1;
OnlyNumbers<void, void, void> o2;
OnlyNumbers<int, int> o3;
but only explicating at least a second template type.
Anyway, why don't you simply avoid to define the main version of the template?
// declared but (main version) not defined
template<typename T, typename = void>
struct OnlyNumbers;
// only specialization defined
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ };
Your code is ill-formed since the primary template cannot be instantiated. See the standard quote in Barry's answer to the related question you linked to. The roundabout way you have used to ensure that the clearly stated standard requirement cannot be met, does not help. Stop fighting your compiler rsp. the standard, and go with Handy999's approach. If you still don't want to do that e.g. for DRY reasons, then a conformant way to achieve your goal would be:
template<typename T, typename Dummy = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(! std::is_same<Dummy, void>::value, "T is not a number type.");
Two remarks:
First, I deliberately replaced the error message because the error message "is not an arithmetic type" screams that you must test ! std::is_arithmetic<T>::value. The approach I've outlined potentially makes sense if you have multiple overloads for "numeric" types, some of which meet the standard's arithmetic type requirements and others might not (e.g. maybe a type from a multiprecision library).
Second, you might object that someone could write e.g. OnlyNumbers<std::string, int> to defeat the static assertion. To which I say, that's their problem. Remember that every time you make something idiot proof, nature makes a better idiot. ;-) Seriously, do make APIs that are easy to use and hard to abuse, but you cannot fix insanity and shouldn't bother trying.
TL;DR: KISS and SWYM (say what you mean)
One of the "critics" of enable_if is that it produces a complete mess of error messages. So e.g. Bjarne Stroustrup wants to introduce Concepts to get around this (see his talk (youtube) or the example (wikipedia)).
My Question: What makes it technically so challenging for compilers to produce readable output with enable_if?
std::enable_if sounds like something special that can enable/disable function templates and class templates given a compile-time boolean expression... but it isn't. Here's a possible implementation:
template <bool TCond, typename T = void>
struct enable_if {};
template <typename T>
struct enable_if<true, T> { using type = T; };
The above means:
If TCond is true, enable_if will define an inner type type alias.
If TCond is false, enable_if will not define an inner type type alias.
When enable_if is being used to enable/disable something, we refer to its ::type internal alias:
template <typename T,
typename = std::enable_if</* something */>::type>
void foo(T) { /* something */ }
If ::type exists for the above /* something */ condition, foo is well-formed. Otherwise, substitution failure occurs, triggering SFINAE.
The compiler isn't aware of our intent and deals with eventual errors by tracing the source of the error. It isn't aware that we meant to enable/disable some function/class in a set of functions/classes - it cannot therefore give us meaningful and intuitive errors. The best it can do is pinpoint the source of the error and give us a backtrace.
If enable_if was a special keyword or a compiler intrinsic, then the errors would very likely be easier to parse.
Consider the following template:
template <typename T>
void foo() {
static_assert(sizeof(T) == 0, "Now what?");
}
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, […]
(Reference taken from https://stackoverflow.com/a/11048906/560450)
In practice, the static_assert will not fail until we instantiate the function template foo, because the use othe the template parameter forces the compiler to evalute the condition only during two-phase lookup. Therefore, the code snippet above is not deemed ill-formed unless the function template is instantiated.
On the other hand, sizeof(T) > 0 in C++ by definition. Therefore, the value of the condition is in fact independent of any template parameter! Is a "malicious" compiler allowed to take advantage of this fact, and reject the program as ill-formed, regardless whether foo is actually instantiated or not?
Yes, the compiler is allowed, but not required, to reject this.
§14.6 [temp.res]/p8:
If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.
An easy workaround:
template <typename T>
struct foo_protector : std::false_type {};
template <typename T>
void foo() {
static_assert(foo_protector<T>::value, "Now what?");
}
The compiler is not allowed to reject foo()'s definition because there might be a specialization of foo_protector, not yet seen, such that its value member is true. As long as you don't actually write such a specialization, the static_assert will fire as expected.