Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Argument dependent name lookup and typedef

Tags:

c++

I would not have expected this code to compile, but it does. My understanding is that func(d) it looks in the global namespace for a function called "func" but also in the namespace of any passed in parameters (Argument dependent lookup)

But in this case the parameter is in the global namespace. So why does it find "func" in the ns namespace? Are there special rules saying that if the parameter type is a typedef then it uses the namespace of the underlying type rather than the namespace of the actual parameter?

This appears to be true but I can't find anything supporting this... Is it the expected behavour?

namespace ns
{
    struct data {};
    void func(ns::data item) {}
};

// Create an alias "datatype" in the global namespace for ns::data
typedef ns::data datatype;


int main()
{
    datatype d;
    func(d);
}
like image 264
jcoder Avatar asked Nov 02 '12 09:11

jcoder


People also ask

What is argument-dependent lookup why it is useful?

argument-dependent lookup (ADL) These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup. Argument-dependent lookup makes it possible to use operators defined in a different namespace.

How does ADL work C++?

In the C++ programming language, argument-dependent lookup (ADL), or argument-dependent name lookup, applies to the lookup of an unqualified function name depending on the types of the arguments given to the function call.

What is unqualified call C++?

A function call expression such as func(a,b,c) , in which the function is named without the :: scope operator, is called unqualified. When C++ code refers to a function by an unqualified name, the compiler performs a search for a matching function declaration.


2 Answers

The other answers already provide the reason, if not the rationale:

A typedef is an alias to a type, and it will be resolved by the compiler to the actual type. Argument dependent lookup is done based on the underlying type, not the typedef.

The rationale for this design decision is actually the reason why ADL is in the language. ADL was added to the language to support operator overloading. In any other use case, the user can explicitly state the namespace of the functions, but in the case of operator overloading that would lead to convoluted code that is counter intuitive:

std::string s("Hi");
std::cout.operator<<(s); // or is it std::operator<<(std::cout,s)??

So the language added rules for lookup to find operators (and functions) in different namespaces, and in particular in the namespace of the arguments to the function. In this case inside std:: in case the operator<< that takes a std::string is not a member of std::cout. The same behavior is extended to all free functions in the same namespace (why not?) allowing the interface of a type to include not only member functions but also free functions in the same namespace.

Now if you focus on this, the purpose is accessing functions that are part of the interface of the type, and those are defined with the type. When you add a typedef in a different namespace you are just creating a shorthand to refer to the original type. All of the functions that were provided with the type (for example operator<<(std::ostream&,MyType)) are in the original namespace, no in the namespace of the typedef. You want ADL to look into the namespace where the real type was defined, not where the alias was created.

like image 145
David Rodríguez - dribeas Avatar answered Oct 07 '22 10:10

David Rodríguez - dribeas


This behaviour is specified by the standard (my emphasis):

3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

2 - 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.

This is somewhat unfortunate; it means that e.g. the code (adapted from ADL with typedefs from another namespace):

std::vector<int> v;
count(v.begin(), v.end(), 0);

will depend for its validity and meaning on whether std::vector<T>::iterator is a typedef to T * or to some type in namespace std.

like image 27
ecatmur Avatar answered Oct 07 '22 11:10

ecatmur