When defining a prototype of an overloaded C ++ function template, is it possible to use its name to indicate previous definitions?

Let's say we want to overload the function template f, but only if such an overload has not yet been declared:

template<typename T> void f(T); // main prototype struct A {}; struct B {}; //we want to declare B f(A), but only if something like A f(A) hasn't been declared //we can try to check the type of expression f(A) before defining it //and disable overload via enable_if template<typename T = void> //it has to be a template to use enable_if std::enable_if_t<std::is_same_v<void, decltype(T(), (f)(A{}))>, B> f(A); // decltype expression depends on T, but always evaluates to the type of (f)(A{}) // parenthesis are used to disable ADL, so only preceding definitions are visible 

The code is accepted by the Clan, is working on GCC (see https://godbolt.org/g/ZGfbDW ), as well as a too complex context for a recursive type error or compiler function in Visual C ++ 15.5.

My question is: is this a legal declaration in accordance with the C ++ standard or does it include undefined behavior?

+5
source share
1 answer

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); // ^^^ ^^^ // #1 #2 

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 } 
+2
source

Source: https://habr.com/ru/post/1273875/


All Articles