Given the following two overloads
template<class T_ret, class... Args>
int test(T_ret (*func)(Args...)) { return 1; }
template<typename T>
int test(const T &lambda) { return 2; }
there is a difference in behaviour between MSVC and gcc /clang when passing a function with the noexcept specifier.
In the following example:
void func() noexcept {}
int main()
{
return test(&func);
}
gcc and clang take the second overload, while MSVC takes the first overload (https://godbolt.org/z/eah3r7E8r). A more specific overload adding the noexcept specifier is found by all compilers:
template<class T_ret, class... Args>
int test(T_ret (*func)(Args...) noexcept) { return 3; }
Which overload resolution is correct in this case?
>Solution :
That’s a matter of what C++ version you are using.
Before C++17 noexcept was not part of the function type or the function pointer type. Therefore both functions could take the argument without any type conversion and partial ordering of function templates would break the tie in overload resolution towards the first overload, which is more specialized (it accepts only function pointers, while the second overload accepts any type).
Since C++17 noexcept is part of the function type and the function pointer type and a conversion which removes the noexcept is a non-identity conversion. Your first overload has no noexcept qualifier on the function pointer type in the parameter and therefore requires this conversion, making it a worse choice in ranking of the conversion sequences than the identity conversion sequence required for the second overload.
MSVC is defaulting to some standard before C++17, while recent GCC are defaulting to C++17, hence the difference in your compiler explorer test. They agree if you actually set the version.