I believe that this is a legal expression based simply on the fact that there really is no reason for it not to be.
A couple of things are worth pointing out. First, here we have two uses of f , and they are different:
template<typename T = void> std::enable_if_t<std::is_same_v<void, decltype(T(), (f)(A{}))>, B> f(A);
We declare the name f in #2 , but it does not have the ability to be used in #1 & dagger; - its announcement point is after a full announcement, which includes the enable_if_t block. Therefore, there is no recursion. If VS has problems with this, I suspect that this may be due to their common problems around finding names in templates.
Secondly, these are not functionally equivalent templates - the main prototype takes an argument, the type of which is a template parameter, and this one takes A The point of functional equivalence is to match declarations with definitions so as not to require a copy of token tokens, but our two function templates f in this example are completely different.
I see no reason for this to be poorly formed. Yes. Twisted, no.
& dagger; This leads to a fairly common error when trying to declare a recursive function template whose return type depends on itself. eg:.
template <size_t V> using size_ = std::integral_constant<size_t, V>; constexpr size_<0> count() { return {}; } template <typename T, typename... Ts> constexpr auto count(T, Ts... ts) -> size_<decltype(count(ts...))::value + 1> { return {}; } int main() { static_assert(count() == 0); // ok static_assert(count(1) == 1); // ok static_assert(count(1, 2) == 2); // error: no matching function to call }
source share