Home > Net >  Deleted function template in requires-expression, differing compiler behavior
Deleted function template in requires-expression, differing compiler behavior

Time:11-09

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.

CodePudding user response:

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.

  • Related