2

Consider a function template f<T> with the primary template deleted but some specializations defined. We can use a requires-expression to test if f<T> has been specialized:

template <typename T>
int f() = delete;

template <>
int f<int>() {
    return 1;
}

template <typename T>
int g() {
    if constexpr (requires { f<T>; }) {
        return f<T>();
    } else {
        return -1;
    }
}

int main() { return g<void>(); }

Version 1 (godbolt link), same as above: compiles on clang but not gcc or msvc.

Version 2 (godbolt link): replacing requires { f<T>; } with requires { f<T>(); } it now compiles on gcc as well.

Version 3 (godbolt link): replacing the inline requires-expression with a named concept, it now compiles on all three compilers.

I am curious which of these versions are well-formed C++20 code and which instances represent compiler bugs.

jcai
  • 3,448
  • 3
  • 21
  • 36
  • Seems a little odd, when you could just have the primary template return -1, then you wouldn't even need the `if constexpr` anymore: `template int f() { return -1; } ... template <> int f() { return 1; } ... template int g() { return f(); } ... int main() { return g(); }` – Remy Lebeau Nov 04 '22 at 17:22
  • That is true. In my actual use case, I was doing a compile-time loop over many possible `f`, where only a few `f` were actually implemented. When I had a default body in the primary template, the compiler was generating that code repeatedly. I wanted to avoid this. – jcai Nov 04 '22 at 17:33
  • if you try to use `f`, then suddenly only msvc accept the code. https://godbolt.org/z/P34eWK18W – apple apple Nov 04 '22 at 17:53
  • @appleapple that code is not using `f`, though. It is using `f`, which is `delete`'d. MSVC compiles that code because it is optimizing out the usage. The code is not *calling* `f()`, it is only *referring* to `f` by name, which decays into a pointer to the function. The pointer is not being used for anything, thus *can* be optimized away, which MSVC is doing (and issuing a warning), but gcc and clang are not doing. – Remy Lebeau Nov 04 '22 at 20:20
  • @RemyLebeau ah yes my typo. it's `f`, just like what OP put inside `require` expression. – apple apple Nov 04 '22 at 21:06
  • @RemyLebeau I don't think compiler can optimize ill-formed program and ignore the error. (unless it's NDR, of course) – apple apple Nov 04 '22 at 21:08
  • @RemyLebeau yes then it's merely different behavior of that expression, not `require` expression. I just recheck the outout (of first link in question), and find gcc *report an error* on `require` expression and \*also\* compile the `constexpr` body. – apple apple Nov 04 '22 at 21:29

1 Answers1

1

Well, MSVC is definitely wrong. It thinks the requires-expression is actually true. But [dcl.fct.def.delete]/2 says:

A program that refers to a deleted function implicitly or explicitly, other than to declare, it, is ill-formed.
[Note 1: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. For an overload set, only the function selected by overload resolution is referenced. The implicit odr-use ([basic.def.odr]) of a virtual function does not, by itself, constitute a reference. — end note]

The expression f<T> is an lvalue that refers to the deleted function, so it "refers" to it; this is ill-formed and it should be detected by the requires-expression.

The only question is whether the ill-formedness is actually in the immediate context. The standard doesn't explain what "immediate context" means, but going off the commonly understood meaning, the issue is whether the ill-formedness is the instantiation of the deleted f<void> (which would not be in the immediate context) or the reference to that deleted function (which would be in the immediate context). I'm inclined to go with Clang here: the instantiation itself is not ill-formed, because its effect is to instantiate f<void>, i.e., synthesize the definition of that specialization (and a definition is a declaration), rather than refer to it.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312