Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to implement always_false in the C++ standard library?

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)?

like image 695
Max Langhof Avatar asked Sep 04 '19 11:09

Max Langhof


2 Answers

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{};
like image 145
Jarod42 Avatar answered Oct 07 '22 09:10

Jarod42


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.

like image 40
StoryTeller - Unslander Monica Avatar answered Oct 07 '22 10:10

StoryTeller - Unslander Monica