5

I have never gotten a great explanation of how template argument deduction really works, so I'm not sure how to explain behavior I'm seeing in the following:

template<typename T>
struct Base
{
protected:
    template<bool aBool = true>
    static void Bar(int)
    {
    }
};

template<typename T>
class Derived : public Base<T>
{
public: 
    void Foo() { Base<T>::Bar<false>(5); } 
};

int main()
{
    Derived<int> v;
    v.Foo();
    return 0;
}

This code won't build, and gives the error:

main.cpp: In instantiation of 'void Derived<T>::Foo() [with T = int]':
main.cpp:25:8:   required from here main.cpp:19:15: error: invalid
operands of types '<unresolved overloaded function type>' and 'bool'
to binary 'operator<'

If you change the 2 Base<T>s in Derived to Base<int>, it compiles. If you change the call to Bar() to Base<T>::template Bar<false>(5);, it also compiles.

The one-liner I saw as an explanation for this is that the compiler doesn't know that Bar is a template, presumably because it doesn't know what Base is until a specialization of Derived is declared. But once the compiler starts generating code for Foo(), Base<T> has already been defined, and the type of Bar can be determined. What is causing the compiler to assume the symbol Bar is not a template, and attempting to apply operator<() instead?

I assume it has to do with the rules of when templates are evaluated in the compilation process - I guess what I'm looking for is a good comprehensive explanation of this process, such that the next time I run into code like the below, I can deduce the answer without the help of the good people on stack overflow.

Note I'm compiling with g++ 4.7, with c++x11 support.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Rollie
  • 4,391
  • 3
  • 33
  • 55
  • possible duplicate of [Where and why do I have to put the "template" and "typename" keywords?](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) – Mat Oct 11 '12 at 20:49
  • i think my FAQ answer doesnt explain why temlates are not just parsed when instantiated (which is what is being asked here, if i understand correctly). my FAQ takes as a given that compilers parse templates early, when they are defined. so i dont know whether the dupe link is really alright. – Johannes Schaub - litb Oct 11 '12 at 21:46
  • Just finished reading it, still really good info even if not the only thing I was looking for! – Rollie Oct 11 '12 at 21:59

1 Answers1

3
void Foo() { Base<T>::Bar<false>(5); } 

In this context Base<T> is a dependent name. To access a member template of a dependent name you need to add the template keyword:

void Foo() { Base<T>::template Bar<false>(5); } 

Otherwise Base<T>::Bar will be parsed as a non-template member and < as less-than.

As of why the template is required, the reason is two-phase lookup. The error is triggered during the first pass, before the type is substituted, so the compiler does not know what is the definition of Base<T>. Consider for example that you added an specialization of Bar for int that had a non-template Bar member (say for example an int member). Before substituting T into Foo, the compiler does not know if there is an specialization for the type.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • this error is given in the second pass. you can define the base class by explicit specialization in such a way that his exact line compiles. if you define suitable operator<, i think it is even possible to make it compile without touching anythingof those classes. – Johannes Schaub - litb Oct 11 '12 at 21:50
  • @JohannesSchaub-litb: I am a bit lost here... where in the code is `operator<` (intenationally) used? The compiler is complaining about no suitable `operator<` that takes `Base::Bar` and `false` in the code `Base::Bar::Bar` with an argument of 5. – David Rodríguez - dribeas Oct 11 '12 at 21:57
  • I am indeed interested in the why of this more than just making the code compile incorrectly. Re the phase though, I assume it has to be 2nd phase because I can have a specialization for Base that declares a static integer Bar, which could be compared to false (which I just wrote a quick test to verify) – Rollie Oct 11 '12 at 22:06
  • of course whether or not the operator< is used intentionally or not is unknown to the compiler. the compiler cannot complain about it at the definition point, because it doesnt know the types of the arguments yet. – Johannes Schaub - litb Oct 11 '12 at 22:08
  • @Rollie: During the 1st phase the template is verified, non-dependent names are resolved and the *type* of the dependent names fixed (i.e. `Bar` is determined to be a non-type, non-template). It is in this phase that the problem happens, as `Bar` is determined to be a non-template and parsing determines `<` to be a *less-than* comparison. After the first phase completes, the meaning of `<` is fixed to be comparison and during the second phase the compiler encounters `operator<(Base::Bar,false)` [...] – David Rodríguez - dribeas Oct 11 '12 at 22:11
  • [...] The first argument to the comparison is `Base::Bar` that is a template function (at this point it is known), which in this context cannot be resolved to any particular instantiation, and thus is *unresolved overloaded function type*, the second argument is `false` which is of type `bool` and you get your error message. – David Rodríguez - dribeas Oct 11 '12 at 22:13
  • @JohannesSchaub-litb: The compiler *knows* (read lacking `template` in the code *must assume*) that `Bar` is not a template and thus during the first phase `<` is determined to be `operator<`. But the problem is that during the first phase, lacking `template` the parsing of the expression is `operator<( Base::Bar, false )`. Your comment says: *if you define suitable operator<*, there is no `operator<` in the code. – David Rodríguez - dribeas Oct 11 '12 at 22:17
  • @DavidRodríguez-dribeas I see! good explanation. I guess now I want to know why the type of any particular name is set during the first phase of the lookup, but that is different enough to warrant another question (which you are of course welcome to answer as well, should you know) – Rollie Oct 15 '12 at 02:19