Exactly when are templates expanded?

C++ templates are only expanded as necessary. The following is error-free even though does_not_exist() does indeed not exist. The method never_called() is never called and thus never expanded, and so does_not_exist() is never needed.

  template< typename X >
  struct
use_non_existant_stuff
  {
    int never_called( )  { does_not_exist( ); }
  };

// no errors or warnings here
use_non_existant_stuff< int > inst_that_does_not_use_methods;

But what about when a template method is expanded. When are the identifiers resolved? Consider the class uses_hidden_fn<..>.

  template< typename X >
  struct
uses_hidden_fn
  {
    X x;
    int call_hidden_fn( )  { return hidden_fn( x); }
  };

namespace hiding_ns {
int hidden_fn( double);
}

// This is OK.
uses_hidden_fn< double > inst_double;

// This fails because hidden_fn( double) is not visible.
int a = inst_double.call_hidden_fn( );

Invoking the method call_hidden_fn() fails because although hidden_fn(double) is defined, it is hidden in the hiding_ns namespace.

You can solve this problem with a simple using hiding_ns::hidden_fn; declaration. And hidden_fn(double) doesn’t have to be exposed before it is needed, only before the end of the compilation unit. The following compiles without error or warning.

  template< typename X >
  struct
uses_hidden_fn
  {
    X x;
    int call_hidden_fn( )  { return hidden_fn( x); }
  };

// This is OK.
uses_hidden_fn< double > inst_double;

// This is OK because hidden_fn( double) is declared below.
int a = inst_double.call_hidden_fn( );

// Declare hidden_fn(double) after we needed it above.
namespace hiding_ns {
int hidden_fn( double);
}
using hiding_ns::hidden_fn;

I’m testing this on the Microsoft (ms9) compiler, but I think it’s standard that templates are not expanded and identifiers are not bound until the end of the compilation unit.

This also compiles without complaint.

  template< typename X >
  struct
uses_hidden_fn
  {
    X x;
    int call_hidden_fn( )  { return hidden_fn( x); }
    int call_hidden_fn2( ) { return hidden_fn2( ); }
  };

namespace hiding_ns {
struct hidden_type { };
}

uses_hidden_fn< hiding_ns::hidden_type > inst;
int a = inst.call_hidden_fn( );
int b = inst.call_hidden_fn2( );

// Declare hidden_fn(..) after we needed it above.
namespace hiding_ns {
int hidden_fn( hidden_type &);
int hidden_fn2( );
}
using hiding_ns::hidden_fn2; // needed
//using hiding_ns::hidden_fn; // not needed!

I was surprised. I thought that we’d still need the using hiding_ns::hidden_fn; declaration at the bottom to make this work, but the (MS) compiler doesn’t complain. Apparently calling hidden_fn(..) with an argument whose type is in the hiding_ns namespace is enough of a hint for the compiler to dig hiding_ns::hidden_fn(hidden_type&) out of that namespace.

If you declare another hidden_fn(hiding_ns::hidden_type&) at top namespace scope the compiler complains that there are two hidden_fn(..)s to choose from. So it does not prefer the exposed declaration over the hidden one.

Although this sort of behavior is interesting, I would recommend strongly against relying on it. Even if it is standard (is it?), it feels like something on the edge. It is clearer to declare hiding_ns::hidden_fn(..) near the top of the file, followed by a using statement. That way hidden_fn(..) is exposed before uses_hidden_fn<..> is expanded.

Comments

3 Responses to “Exactly when are templates expanded?”

  1. Brent on January 10th, 2009 8:10 pm

    This sounds like “argument-dependent name lookup” which is part of the standard. See: http://www.open-std.org/jtc1/sc22/open/n2356/basic.html#basic.lookup.koenig

  2. Neal on January 12th, 2009 11:10 am

    Brent, thanks for the link. Looks like you’re right about not having to declare “using hidden_ns::hidden_fn”. Under 3.4.2 it says:

    2 For each argument type T in the function call, there is a set of zero or more associated namespaces to be considered. The set of namespaces is determined entirely by the types of the function arguments.
    … snip …
    If T is a class type, its associated namespaces are the namespaces in which the class and its direct and indirect base classes are defined.

    Good catch.

  3. CandaceKramer34 on June 8th, 2010 8:16 am

    Some time ago, I needed to buy a house for my corporation but I did not earn enough cash and couldn’t purchase something. Thank God my fellow adviced to get the business loans from trustworthy creditors. Thence, I did so and used to be happy with my financial loan.