3

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:

  1. 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.

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • I don't see how a user defined `enable_if` will help. `enable_if` depends on a value, not a type so you can only specialize it for `true` and `false`. – NathanOliver Dec 28 '20 at 15:54
  • 1
    @NathanOliver The second parameter of a custom `enable_if` is still a template parameter that can be specialized on, as in the second snippet. It's just that we usually don't think of it that way. – Passer By Dec 28 '20 at 15:57
  • Aha. I suppose you could do that, but then you're not really doing what you say your doing. `enable_if` should evaluate to nothing so it works correctly. Sounds like what you really need is `std::conditionial`, not `std::enable_if`. – NathanOliver Dec 28 '20 at 16:02
  • @NathanOliver This is language-lawyering, with the most convoluted possible combination of rules :P It did happen in practice, and I really meant `std::enable_if` and not `std::conditional`. See [this](https://godbolt.org/z/Mn3hdx) as an example. There are even more bizarre rules surrounding that though. – Passer By Dec 28 '20 at 16:05
  • 1
    If you're not allowed to do a thing, then doing the thing you're not allowed to do is illegal. From that point forward, it no longer matters what *else* you may have done. I don't understand what the question is. – Nicol Bolas Dec 28 '20 at 16:13
  • @NicolBolas The question is about how exactly these rules work together, which may eventually lead to confusing behaviour. IMO neither is an ideal state of affairs. One is the (arguably?) intended behaviour, the other is surprising, unless laid out bare like this. – Passer By Dec 28 '20 at 16:22
  • 1
    As specified in [standard](https://eel.is/c++draft/meta#trans.other), `std::enable_if` cannot have dummy specialization from compiler to have an (unreachable) valid type. – Jarod42 Dec 28 '20 at 16:41
  • @PasserBy: There is no "work together". If you rob a bank, whether you stole the getaway car isn't important for determining if your actions were criminal. It may add to your slate of charges, but that's irrelevant to the question of whether you're in jail. – Nicol Bolas Dec 28 '20 at 17:21
  • Technically, dup of https://stackoverflow.com/questions/38834149/how-does-using-ellipses-for-sfinae-work – Language Lawyer Dec 29 '20 at 00:53
  • @LanguageLawyer It didn't explicitly mention [meta.rqmts]/4, otherwise it's indeed technically a dupe, with an opposite answer to this one (yikes). – Passer By Dec 29 '20 at 09:00
  • @NicolBolas I don't think it's as clear cut as your analogy. Even if it is, surprising behaviour is worth questioning. To arrive at the conclusion you had to 1. realize there's [temp.res] 2. realize specializations bails you out 3. realize there's [meta.rqmts]. Every time you dig deeper, the validity of the program flips, and it's all NDR. People also generally don't expect a standard library template that's obviously user-implementable to behave differently to the extent it causes UB. – Passer By Dec 29 '20 at 16:51
  • @PasserBy: There is only "deeper" if you didn't know about [meta.rqmts] to begin with. If you started from there, then it's clear that 1 and 2 are irrelevant because you already broke 3. – Nicol Bolas Dec 29 '20 at 17:25

1 Answers1

6

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.
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458