0

AFAIK there are only two 'stages' of template instantiation:

  • default (immediate),
  • deferred (lazy).

Default is used to for following members:

  • typedefs,
  • member fields.

Deferred:

  • functions (static or not),
  • nested types.

Is this correct, or there are some other gotchas/rules concerning compiler eagerness to instantiate templates?

Red XIII
  • 5,771
  • 4
  • 28
  • 29
  • 1
    Some compilers, [notably the MS VC++ compiler](http://stackoverflow.com/questions/6273176/what-exactly-is-broken-with-microsoft-visual-cs-two-phase-template-instant), do not implement the two-phase name lookup for template instantiation correctly. Also, [check out this post about two-phase name lookup](http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html) from the LLVM authors. – In silico Mar 26 '13 at 08:42

1 Answers1

5

Totally incorrect. There is only one stage of template instantiation. When (and where, which is an important point) depends on how it is used. I think you're confounding name lookup with instantiation.

Name lookup is done in two phases. (At least if the compiler is conform—VC++ still gets this wrong.) The first occurs when the compiler parsed the template definition; the second when the template is instantiated. Whether a symbol is looked up in the first phase or the second depends on whether it is dependent or not. The rules determining this are fairly complicated, but in general:

  • If it is a function name, and one or more of its arguments depend on a template argument, the function name is dependent, and it will be looked up (and function overload resolution) will occur on instantiation, and not before.

  • If the name is qualified by a template argument (e.g. T::something, where T is a template argument), it is dependent.

  • In a member of a class template, if there is a dependent base class (a base class which in some way depends on the template argument), anything to the right of this-> is dependent.

(The actual rules are considerably more complicated, but the above is a rough approximation, and probably sufficient for most uses.)

EDIT:

Just some examples of the difference:

class MyType {};

void func0( double d )
{
    std::cout << "called func0( double )" << std::endl;
}

void func1( double, MyType const& )
{
    std::cout << "called func1( double )" << std::endl;
}

template <typename T>
int funcT( T const& param )
{
    func0( 42 );            //  non-dependent
    func1( 42, param );     //  dependent
}

void func0( int d )
{
    std::cout << "called func0( int )" << std::endl;
}

void func1( int, MyType const& )
{
    std::cout << "called func1( int )" << std::endl;
}

int
main()
{
    funcT( MyType() );
    return 0;
}

The output should be

called func0( double )
called func1( int )

The call to func0 in funcT is non-dependent, so name lookup occurs only at the point where the template was defined, and not later, when it is instantiated. At that point, there is only one func0, so it gets called.

The call to func1 infuncT is dependent (since its second argument depends on the template argument), so there will be an additional name lookup at the point of instantiation (immediately following main, in this case). This additional lookup only uses ADN, but since one of the arguments is defined in global namespace, ADN will look in global namespace, and find the second declaration of func1 as well (which will then be chosen by overload resolution, because it is a better match).

Note that I can't verify this, because at the moment, I only have access to VC++11, which is broken. And it is subtle, so it's quite possible I've missed something. The general rule is to avoid such ambiguities.

Note that if func0 were not declared before the template definition, the code should not compile. From experience, when moving from pre-standard compilers (or VC++), this is the most common cause of errors.

Another common case where this sometimes surprises:

template <typename Base>
class Derived : public Base
{
public:
    Derived()
    {
        init();         //  Non-dependent lookup, will NOT find any
                        //  function init() in Base!!!
        this->init()    //  Dependent lookup, WILL find
                        //  Base::init, if it exists.
        Base::init()    //  Also dependent.
    }
};

Typically, if you're using this pattern, and developing on a pre-standard compiler like VC++, you'll get a number of compiler errors when you move to a more modern compiler. They're easily fixed by adding the this->. However, if there is also a global function init visible when the template is defined, you will call this, and not the function in the base class. If you choose meaningful names for your functions, and put global functions in namespaces, you'll probably not get hit very often by this silent change of semantics, but bewarned.

James Kanze
  • 150,581
  • 18
  • 184
  • 329