tags:

views:

164

answers:

4

I was reading about the template name resolution here. Just to get the feel of the things I replicated the code like this:

void f (char c)
{
    std::cout<<"f(char)\n";
}
template <class T>
void g(T t)
{
    f(1);
    f(T(1));
    f(t);

    d++;
}
double d;
void f(int n)
{
    std::cout<<"f(int)\n";
}

void test()
{
    std::cout<<"First call\n";
    g(1);

    std::cout<<"Second call\n";
    g('a');
}

int main()
{
    test();
    return 0;
}

According to the article linked I should have a compiler error for the d++ statement. Also for the first call of g(1), I should have one call of f(char) followed by two calls of f(int) and for the second call I should get three calls of f(char). However, when I compiled this using Vs2008, it compiled fine without any errors. Also, the output was:

First call

f(int)

f(int)

f(int)

Second call

f(int)

f(char)

f(char)

I am now wondering which one is correct? The article I am linking to or the VS2008 output? Any clue which is correct?

+3  A: 

The article is correct about whether d++ should compile.

Visual C++ does not do two-phase template instantiation - it does pretty much all its parsing at instatiation time.

Gcc and Comeau will give the correct error and will call the correct f.

James Hopkin
..and I thought VS2008 is really compliant when it came to templates.
Naveen
The article is incorrect. See my answer below.
Johannes Schaub - litb
@litb: I should have been more specific about which bit of the article I was talking about (I didn't read it all in detail). Amended my answer now.
James Hopkin
@litb: never expected your answer to get to the top, did you? :)
xtofl
@xtofl, i feel guilty now having used a relative wording :( @James, oh that, yeah i'm afraid d++ should fail big times :p
Johannes Schaub - litb
+3  A: 

Even though VS2008 was considered the most standard-compliant C++ compiler of it's time, here we have one instance where it's accepting invalid code. G++ doesn't compile this (d++: error: 'd' was not declared in this scope). That said, any program that relies this much on the intricacies of the C++ language is broken anyway :)

+3  A: 

Also for the first call of g(1), I should have one call of f(char) followed by two calls of f(int) and for the second call I should get three calls of f(char).

This is not the expected result with a Standard compliant compiler. Since both time you call it with a fundamental type, you will not get name lookup at the instantiation context for the function call.

Name lookup at instantiation context only happens for argument dependent lookup (called lookup using associated namespaces). Both of int and char do not have argument dependent lookup, an thus all the function calls you do will call f(char) after you removed the d++ line.


Since i understand you won't possibly just believe me, here is the Standard quote, out of 14.6.4.2:

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

Note that the article uses a defective example of the Standard (at 14.6/9) (and note that examples are non-normative: They can't change or state rules. Their purpose entirely is illustrative). The defect has been fixed and is incorporated into the upcoming Standard. See this defect report.


As you see, you will have to change from int / char to some user defined type (enums or classes) too see the effect of lookup in instantiation context. Read this answer Order affects visibility for more information.

Johannes Schaub - litb
Excellent - I hadn't read that defect report. It's a little more subtle than I thought.
James Hopkin
If I've read the DR correctly, it seems they've gone for a 'quick fix' - make the example fit the wording - but actually the behaviour is inconsistent, and I'd guess hard to implement.
James Hopkin
@James i don't know why ordinary name lookup doesn't work at instantiation context. But i guess that would make the instantiation context too much dependent on the position the call happens. You as a user of the template function would have to consider every candidate function in the current scope. Now, with only ADL working, you only have to consider the argument's associated namespaces and classes.
Johannes Schaub - litb
That's true, but I think it's an inconsistency that doesn't buy very much. I can't think of any sane examples where you'd overload an existing template function for a built-in type.
James Hopkin
By 'hard to implement' btw, I'm thinking that the compiler has to keep track of all the fs that were defined until g's definition point so that it can pick the right one when it's instantiated with a built-in type.
James Hopkin
+1  A: 

VS should have complained about d++ as d should be looked up at point of definition.

The article is incorrect, the calls should be resolved to f(char) as point of instantiation lookup should not be done for fundamental types (see tlib answer).

g++ behaviour depends on the version:

  • before 3.4, it does always point of instantiation lookup, which is a bug.

  • starting with 3.4, it does point of definition lookup for d; but it does point of instantiation lookup for fundamental types when they are a template parameter, which is a bug.

  • starting with 4.1, it doesn't do point of instantiation lookup for basic types anymore.

AProgrammer