Is it possible to implement always_false in the C++ standard library?
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.