Reputation: 1261
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
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:
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]).
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