Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different behavior for qualified and unqualified name lookup for template

How should this code behave? It calls generic function ignoring my overload if I use qualified name in call_read() function; and it calls overload first and then generic version if I use unqualified name. What's the difference? Is it a bug in GCC?

#include <iostream>

struct info1 {};
struct info2 {};

template<class T> void read(T& x)
{
   std::cout << "generic" << std::endl;
}

template<class T> void call_read(T& x)
{
   ::read(x); // if I replace ::read(x) with read(x) the overload is called
}

void read(info1& x)
{
   std::cout << "overload" << std::endl;
}

int main()
{
   info1 x;
   info2 y;
   call_read(x);
   call_read(y);
}

I also noticed that it works different for fundamental types. See the code bellow

#include <iostream>

typedef struct info1 {};
typedef struct info2 {};
typedef int info3;
typedef double info4;

template<class T> void read(T x)
{
    std::cout << "generic" << std::endl;
}

template<class T> void call_read(T x)
{
    read(x);
}

void read(info1 x)
{
    std::cout << "overload" << std::endl;
}
void read(info3 x)
{
    std::cout << "overload" << std::endl;
}

int main()
{
    call_read(info1());
    call_read(info2());
    call_read(info3());
    call_read(info4());
}

It is supposed to call overloaded function twice, but it's not. See the result here http://codepad.org/iFOOFD52

like image 610
axe Avatar asked Dec 14 '11 08:12

axe


People also ask

What does unqualified name mean?

An unqualified name is simply the class, interface, enum or field without package information.

What is unqualified name in 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.

What is name lookup?

Name lookup is the procedure by which a name, when encountered in a program, is associated with the declaration that introduced it. For example, to compile std::cout << std::endl;, the compiler performs: unqualified name lookup for the name std , which finds the declaration of namespace std in the header <iostream>


2 Answers

What you're observing is a superposition of two-phase name lookup and argument dependent lookup.

Let's see what the standard says (C++03). [temp.dep]:

[...] In an expression of the form:

postfix-expression ( expression-listopt )

where the postfix-expression is an identifier, the identifier denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2).

That means that in both read and ::read, read is a dependent name because x is type-dependent. That means that it's resolved at the point of instantiation. Let's see what are the rules for this [temp.dep.candidate]:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found.

Therefore for the ::read case only functions declared before the template definition are considered. But:

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

for the unqualified read both functions are considered, those visible at template definition and template instantiation.

like image 70
Yakov Galka Avatar answered Oct 21 '22 02:10

Yakov Galka


Yes, this is the expected behaviour. In the first case (::read) you effectivly disable ADL (argument dependent lookup) which restricts name lookup to things that have been declared in the global scope before your use of read. If you remove :: ADL will kick which may resolve to functions you declared after your function template.

Edit: And since for fundamental types like int and double there is no ADL, this explains your 2nd observation.

like image 36
sellibitze Avatar answered Oct 21 '22 01:10

sellibitze