plexando
plexando

Reputation: 1261

Bizarre behavior of std::is_invocable

I have an issue with std::is_invocable in the following program:

#include <iostream>
#include <type_traits>

void g() {}

template<bool B, typename T>
void f(T t) {
    if constexpr (B)
        g(t);
}

int main() {
    std::cerr << std::boolalpha <<
        std::is_invocable_v<decltype(&f<true, int>), int> << std::endl;
}

I would have expected the program output to be false since f<true, int> cannot be instantiated. However, GCC (as of 10.0.1 20200224) does not compile with the error message being

test.cpp: In instantiation of 'void f(T) [with bool B = true; T = int]':
test.cpp:14:51:   required from here
test.cpp:9:10: error: too many arguments to function 'void g()'
    9 |         g(t);
      |         ~^~~
test.cpp:4:6: note: declared here
    4 | void g() {}
      |  

and Clang (as of 11.0.0) even prints true.

What is the correct behavior in this case?

Upvotes: 3

Views: 563

Answers (1)

Barry
Barry

Reputation: 303357

Question boils down to: does decltype(&f<true, int>) actually instantiate f<true, int>?

If yes, then the program is ill-formed because that instantiation is ill-formed (can't invoke g with an int, as requested).

If no, then the correct answer for invocable_v is true - since there is no SFINAE on the body of a function, and the signature of the function clearly allows this invocation.

It appears that gcc thinks yes (and hard errors) and clang thinks no (and yields true). I think clang is correct here. The rules we have are:

[temp.inst]/10:

If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated ([temp.over]).

[temp.inst]/11:

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.

We do involve overload resolution here (address of function), but this is just about instantiating the declaration of the function template. I don't think anything requires instantiating the definition of the function template (f returns void, not something like auto), so gcc is instantiating it too eagerly here.

That said, certainly it should not yield false.

Upvotes: 10

Related Questions