Dependent Name Resolution & Namespace Std/Standard Library

Dependent name resolution & namespace std / Standard Library

The problem here is that the point where your call to operator >> is being made is somewhere inside the std namespace, and the namespace where the types of the arguments live is std.

Provided the compiler can find an operator >> in either the namespace where the call occurs or the namespace where the types of the arguments live (both are the std namespace in this case), no matter whether it is viable or not for overload resolution (which is performed after name lookup), it won't bother looking for more overloads of operator >> in parent namespaces.

Unfortunately, your operator >> lives in the global namespace and is, therefore, not found.

Template dependent name resolution should not find declarations with no linkage?

This is a defect in the standard. Originally addressed in core issue 561, where the committee judged that

Notes from the April, 2006 meeting:

The consensus of the group was [..] that internal-linkage functions should be found by the lookup (although they may result in errors if selected by overload resolution).

Unfortunately the corresponding fix was insufficient, as elaborated in core issue 1258:

C++11 expanded the lookup rules for dependent function calls (17.7.4.2 [temp.dep.candidate] paragraph 1 bullet 2) to include functions with internal linkage; previously only functions with external linkage were considered. However, 17.7.4.1 [temp.point] paragraph 6 still says,

The instantiation context of an expression that depends on the template arguments is the set of declarations with external linkage declared prior to the point of instantiation of the template specialization in the same translation unit.

Presumably this wording was overlooked and should be harmonized with the new specification.

That is, the previous wording of your second quoted paragraph was

For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage
found in either the template definition context or the template instantiation context are found.

.. which was amended for C++11, but that change missed your first quote, making it rather pointless. The intent is that functions with internal linkage are not discriminated.

Is it a defect in the standard about dependent name resolution for template

To complete language-lawyer comments, in §"Unknown specializations" of this page

Within a template definition, certain names are deduced to belong to
an unknown specialization, in particular,

  • a qualified name, if any name that appears to the left of :: is a dependent type that is not a member of the current instantiation
  • ...

In your struct TMP,

  • the type T is indeed a dependent type
  • the expression typename T::type is a qualified name and the left if :: is a dependent type
  • that expression becomes an unknown specialization

Then it is said:

Members of unknown specialization are always dependent, and are looked up and bound at the point of instantiation as all dependent names

which allows the lookup of TMP<Test>::type at the point of instanciation.

Namespace resolution with operator== in the STL

!(*__first1 == *__first2) takes place in std::operator==, a function template, so it is considered a dependent unqualified function call expression, so during overload resolution only functions found within the definition context of std::operator== and those found via ADL are candidates.

Clearly there are no operator==(const Foo&, const Foo&) declared within the context of the definition of the standard comparison operator. In an argument dependent lookup (ADL) the namespaces of each of the arguments are checked to search for a viable function for the call, so this is why defining operator== inside of ANamespace works.

Should name lookup be deferred for a dependent class/namespace-name in a class-member-access expression?

1

Here is how I think your first case, t.Dependent::f works. First, I believe (means, I am not totally sure) that 14.6.2.1p5 should say "unqualified-id" instead of "id-expression". But independent of that, your name Dependent::f is actually composed out of two names (in the Standard, each nested nested-name-specifier followed by a member name is called "qualified-id", even if grammatically, these are not qualified-id productions. So a name foo::bar::baz is a qualified-id but also contains 1 other "qualified-id" aswell).

Dependent and Dependent::f. The former is not "An id-expression denoting the member in a class member access expression", so you can't simply apply the rule that applies to Dependent::f to apply also to Dependent.

Dependent is therefor non-dependent and albeit it will need to be looked up within a dependent type will have to be found at definition time. I personally think that we should have a clause that says "When looking up a qualified-id where the qualifier is type-dependent, name lookup yields an empty result.", to handle these "force name-lookup to be done immediately" gracefully. So anyway, in the end, I think your first case is ill-formed by not finding Dependent (clause 3.4 can't just decide by itself over the head of clause 14 that the name is actually dependent anyway).

2

For your other case, operator Dependent, things are easier. You again have two names, Dependent and operator Dependent. Again, I found nothing that says that Dependent is a dependent name here (I am not sure whether that would be wrong or not. That's beyond me).

Name lookup comparison (say, the equality function of the name lookup hash table) for operator function names is "they are conversion-function-ids formed with the same type" (3.8). This means that in order to form the name itself (not yet doing name lookup!), you not only have to give the lexical spelling as is the case for identifiers, but you have to provide a type identity, which needs to be provided by Dependent.

That the lookup of the dependent id-expression in t.operator Dependent* is delayed simply means that the semantic type comparison is delayed. Try this one, which should work fine

struct Dependent; // forward decl at global scope
t.operator Dependent*(); // in function template

Your followup

If it is intended that such a name is not dependent, what is the rationale for this decision? I can see that it makes life easier for the implementor if they do not have to defer evaluation of a construct like t.operator X::Dependent* or t.X::Dependent::f where X could be either a namespace or a type name.

I don't know the rationale, but I think you already have given a good point. This looks very much to the rule that skips dependent base classes when looking up unqualified names. And I think what rationale applies for that case applies for this case aswell. It makes it easier to reason on the function template for the programmer, especially.

struct Dependent;

template<typename T>
void f(T t)
{
t.Dependent::f();
t.operator Dependent*();
}

The code looks fine, but if T happens to have a Dependent member, suddenly Dependent would have a different binding (because first we are told to look into t's class, and then into the surrounding scope). Under my current understanding of the templating rules, the above always refers to the surrounding scope's Dependent, so the above code is "safe", regarding to that pitfall.

using namespace std causes boost pointer cast to trigger ADL in c++17 standard

When the compiler sees a < after an identifier in an expression (such as in static_pointer_cast<Animal>(dp)) it needs to figure out whether < refers to the relational less operator or whether it is the start of a template argument list.

If a template is found by usual unqualified name lookup (not ADL), then the latter is assumed to be the case. But what if no template is found?

Before C++20, if the lookup did not find a template, it was assumed that the name does not refer to a template and that the following < is therefore the less operator.

In your original case that wouldn't make sense. For example there is nothing that static_pointer_cast can refer to as operand to <. So it will fail with a more or less clear error message depending on the compiler.

With using namespace std; it may be possible that the compiler implicitly included std::static_pointer_cast (from <memory>) with one of the headers you included explicitly (there is no guarantee) or it may be that the boost headers include <memory>. If that happened, then the unqualified lookup for static_pointer_cast will find it and because it is a template, < after it will be considered to start a template argument list instead of being the relation operator. Then it is clear that the whole expression is an unqualified function call with template argument list.

Since C++20, if the lookup does not find anything or finds any function at all, then it is also assumed that a < following the name introduces a template argument list.

With the interpretation as template argument list and therefore function call, normal unqualified name lookup and ADL for unqualified function calls will be done, which finds both boost::shared_pointer_cast (via ADL) and std::shared_pointer_cast if you are using using namespace std;, but because you are passing a boost::shared_ptr the std version, which requires a std::shared_ptr argument, will not be viable and the boost version will always be chosen by overload resolution. This part is unchanged with C++20.

Note that this is a breaking change in C++20. In some unusual situations you might have wanted < after a function name to actually refer to the relational operator which would now require parenthesizing the function name. See [diff.cpp17.temp] for an example.

Overload resolution looking into namespaces

ADL is not used when explicit template arguments are involved unless you introduce a template function declaration at the call point. You're using an unqualified form of get using a non-type template argument 0, so you need to introduce a template function declaration or use the qualified version of get as std::get<0>(ar).

In standardese [temp.arg.explicit]/8: (emphasis mine)

[ Note: For simple function names, argument dependent lookup (6.4.2) applies even when the function name is not visible within the scope of the call. This is because the call still has the syntactic form of a function call (6.4.1). But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply. If some such name is visible, argument dependent lookup applies and additional function templates may be found in other namespaces.

EDIT:

As @Yakk - Adam Nevraumont has pointed out in the comment, without the presence of the template function declaration, the expression get<0>(ar) will be parsed as (get<0)>(ar), i.e as a serie of comparison expressions instead of a function call.

Namespace causes sub-optimal template overload resolution

With the namespace, doStuff is of course a dependent name.

You are starting from the wrong premise. There is no ADL for a qualified call like DoStuffUtilNamespace::doStuff(ref). [basic.lookup.argdep]/p1, emphasis mine:

When the postfix-expression in a function call (5.2.2) is an
unqualified-id
, other namespaces not considered during the usual
unqualified lookup (3.4.1) may be searched, and in those namespaces,
namespace-scope friend function or function template declarations
(11.3) not otherwise visible may be found.

DoStuffUtilNamespace::doStuff is a qualified-id, not an unqualified-id. ADL doesn't apply.

For this reason, DoStuffUtilNamespace::doStuff is also not a dependent name. [temp.dep]/p1:

In an expression of the form:

postfix-expression ( expression-listopt)


where the postfix-expression is an unqualified-id, the
unqualified-id denotes a dependent name if [...]. If an operand of an operator is a type-dependent expression, the operator also denotes
a dependent name. Such names are unbound and are looked up at the
point of the template instantiation (14.6.4.1) in both the context of
the template definition and the context of the point of instantiation

(The italicization of dependent name indicate that this paragraph is defining the term.)

Instead, per [temp.nondep]/p1:

Non-dependent names used in a template definition are found using the
usual name lookup and bound at the point they are used.

which doesn't find your later overload declaration.


Specialization works because it's still the same function template declaration that's used; you just supplied a different implementation than the default one.


But what exactly does the standard mean by "namespaces associated with
the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together
with the explicit scoping of the scoped_foo instance type within the
namespace, trigger argument-dependent lookup

No. using-declarations do not affect ADL. [basic.lookup.argdep]/p2, emphasis mine:

For each argument type T in the function call, there is a set of
zero or more associated namespaces and a set of zero or more
associated classes to be considered. The sets of namespaces and
classes is determined entirely by the types of the function arguments
(and the namespace of any template template argument).
Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are
determined in the following way:

  • If T is a fundamental type, [...]

  • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its
    direct and indirect base classes. Its associated namespaces are the
    innermost enclosing namespaces of its associated classes. Furthermore,
    if T is a class template specialization, its associated namespaces and
    classes also include: the namespaces and classes associated with the
    types of the template arguments provided for template type parameters
    (excluding template template parameters); the namespaces of which any
    template template arguments are members; and the classes of which any
    member templates used as template template arguments are members. [
    Note: Non-type template arguments do not contribute to the set of associated namespaces. —end note ]

  • [...]

Confusion around function call resolution

The code in the OP is equivalent to this:

using std::swap; // only for name lookup inside ns::swap

namespace ns {
template <typename T>
void swap(T& a, T& b) {
swap(a, b);
}
}

Why? Because using-directives like using namespace std; have a very peculiar behaviour C++14 [namespace.udir]p2:

A using-directive specifies that the names in the nominated namespace
can be used in the scope in which the using-directive appears after
the using-directive. During unqualified name lookup, the names
appear as if they were declared in the nearest enclosing namespace
which contains both the using-directive and the nominated namespace.

The nearest enclosing namespace that contains both namespace std and the block scope of function ns::swap is the global namespace.

Using-declarations such as using std::swap; on the other hand really introduce names into the scope in which they appear, not in some enclosing scope.


The lookup of a function call expression such as swap(a, b) is called unqualified lookup. The identifier swap has not been qualified with any namespace or class name, as opposed to ns::swap, which has been qualified via ns::. Unqualified lookup for potential names of functions consists of two parts: pure unqualified lookup and argument-dependent lookup.

Pure unqualified lookup stops at the nearest enclosing scope that contains the name. In the OP's example, as illustrated by the equivalent transformation shown above, the nearest scope that contains a declaration of the name swap is the namespace ns. The global scope will not be searched, std::swap will not be found via pure unqualified lookup.

Argument-dependent lookup searches all scopes (here: only namespaces and classes) associated with the argument types. For class types, the namespace in which the class has been declared in is an associated scope. Types of the C++ Standard Library such as std::vector<int> are associated with namespace std, hence std::swap can be found via argument-dependent lookup for the expression swap(a, b) if T is a C++ Standard Library type. Similarly, your own class types allow finding a swap function in the namespaces they have been declared in:

namespace N2 {
class MyClass {};
void swap(MyClass&, MyClass&);
}

Therefore, if argument-dependent lookup does not find a better match than pure unqualified lookup, you'll end up calling ns::swap recursively.


The idea behind calling swap unqualified, that is, swap(a, b) instead of std::swap(a, b) is that functions found via argument-dependent lookup are assumed to be more specialized than std::swap. Specializing a function template such as std::swap for your own class template type is impossible (since partial function template specializations are forbidden), and you may not add custom overloads to namespace std. The generic version of std::swap is implemented typically as follows:

template<typename T>
void swap(T& a, T& b)
{
T tmp( move(a) );
a = move(b);
b = move(tmp);
}

This requires a move-construction plus two move-assignments, which might even fall back to copies. Therefore, you can provide a specialized swap function for your own types in the namespaces associated with those types. Your specialized version can make use of certain properties of, or private access to, your own types.



Related Topics



Leave a reply



Submit