Assume the following code:
#include
template
struct Link
{
Link(T&& val) : val(std::forward(val)) {}
This problem is a result of an issue with the point of declaration (1) combined with dependent name lookup (2).
(1) In the declaration
template
constexpr auto RemoveLinks(const Link& link) -> decltype(RemoveLinks(link.val))
the name RemoveLinks
, or more precisely, this overload of RemoveLinks
, is only visible after the complete declarator according to [basic.scope.pdecl]/1. The trailing-return-type is part of the declarator as per [dcl.decl]/4. Also see this answer.
(2) In the expression RemoveLinks(link.val)
, the name RemoveLinks
is dependent as per [temp.dep]/1, as link.val
is dependent.
If we now look up how dependent names are resolved, we find [temp.dep.res]:
In resolving dependent names, names from the following sources are considered:
- Declarations that are visible at the point of definition of the template.
- Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
The first bullet doesn't find the second overload of RemoveLinks
because of the point of declaration (1). The second one doesn't find the overload because the namespace Util
is not associated with any argument. This is why putting everything in the global namespace or in the namespace Util
works as expected (Live example).
For the same reason, using a qualified-id in the trailing-return-type (like -> decltype(Util::RemoveLinks(link.val))
doesn't help here.