Search code examples
c++templatesdependent-name

Why in a template we can use a dependent name without being already declared if not instantiated?


I have written this code to understand template name lookup:

//void bar(int);

template <typename T>
void foo(T x)
{
    bar(x);
}

void bar(int x)
{
    std::cout << "bar(int)\n";
}

template <int>
void foo(int i)
{
    bar(i);
}



int main()
{

    foo<int>(4);


    std::cout << "\ndone!\n";
}

I've commented out the declaration of function bar(int) intentionally. This function bar(int) is used as a dependent name in the template function foo. So it is bound upon instantiation.

  • I've defined bar right after foo and before a specialization of foo<int> so that this latter can see bar(int).

But when I compile the code I get this error:

‘bar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]|. And if I un-comment the declaration of bar(int) it works fine?!

  • If I must declare a name before using it in a template as a dependent-name then why C++ allows that if not instantiated. (the code works if I don't "use" templated function foo)?

      template <typename U>
      void do_it(U u)
      {
          print(u); // not bound until instantiation
      }
    

So what is the idea behind allowing calling print(u) in do_it which was not already declared and will fail upon instantiation?


Solution

  • In your program, this definition:

    template <int>     // not a specialization, just a different
    void foo(int i)    // template with a non-type template parameter
    {
        bar(i);
    }
    

    does not actually define a specialization of the foo primary template. So when you make this call:

    Foo<int>(4);
    

    you end up calling the primary template. Lookup for the name bar doesn't find that name, and you get an error.

    If instead, you actually write a specialization of foo for int like this:

    template <>           // specialization of primary
    void foo<int>(int i)  // template foo with int
    {
        bar(i);
    }
    

    then the call foo<int>(4); is fine, because it calls the specialization, and lookup for bar at that point does find that name.


    Now going back to your program (without the specialization), what happens if foo is never instantiated, because there's no call, for example? Well, the program is still wrong, but the compiler may not tell you about it. This is the behavior that you are describing with print and do_it, and this is formally known as ill-formed, no diagnostic required.