I had a checker function that used the requires
keyword to detect if a target function was defined or not. I wanted to make it work with C++17, so I switched the checker from using requires
to using expression SFINAE.
That’s when I was surprised to discover that if I called both checkers without defining the target function, then defined the target function and checked again, the requires
checker would reuse the old result while the SFINAE checker would update to properly show the target function is defined.
// Compiles on Clang, GCC, and MSVC.
#include <type_traits>
struct MyStruct {};
constexpr bool Checker_SFINAE(...)
{
return false;
}
template <typename T>
constexpr auto Checker_SFINAE(T) -> decltype(Foo(T()), bool())
{
return true;
}
template <typename T>
constexpr auto Checker_requires(T)
{
return std::bool_constant<requires { Foo(T()); }>();
}
static_assert(!Checker_SFINAE(MyStruct()));
static_assert(!Checker_requires(MyStruct()));
void Foo(MyStruct) {}
static_assert(Checker_SFINAE(MyStruct()));
// Will fail is the previous Checker_requires is removed
static_assert(!Checker_requires(MyStruct()));
int main(){}
What’s causing this difference? Both checker functions use a dependent type for their return type, so I would have figured they would behave identically.
More practically, what makes a function template be interpreted differently between instances even when all its template parameters stay the same?