Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

At which point occurs template Instantiation binding?

Tags:

This code is from "C++ programming language" by Bjarne Stroustrup (C.13.8.3 Point of Instantiation Binding)

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

void h()
{
    extern g(double);
    f(2);
}

And he mentions:

Here, the point of instantiation for f() is just before h(), so the g() called in f() is the global g(int) rather than the local g(double). The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member.

void h()
{
    struct X {}; // local structure
    std::vector<X> v; // error: can't use local structure as template parameter
}

My questions are:

  1. Why should the first code work? g() is declared later, and I really get an error with G++ 4.9.2 that g isn't declared at that point.

  2. extern g(double) - how this works? since return value doesn't matter in case of function overloading, then we can miss it in forward declarations?

  3. the point of instantiation for f() is just before h() - why? isn't it logical that it'll get instantiated when f(2) is being called? Right where we call it, whence g(double) will be in scope already.

  4. The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member - Has this changed in C++14? I'm getting error with C++(G++ 4.9.2), but don't get error with C++14(G++ 4.9.2).

like image 553
user1289 Avatar asked Oct 10 '16 13:10

user1289


People also ask

What is template instantiation?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation to handle a specific set of template arguments is called a specialization.

Are templates instantiated at compile time?

Templates are instantiated in the process of converting each translated translation unit into an instantiation unit. A translation unit is essentially a source file. A translated translation unit (try to say that three times fast) is the output from compilation without templates instantiated.

How do I force a template instantiation?

To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.

Why we need to instantiate the template?

In order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).


1 Answers

"In 1985, the first edition of The C++ Programming Language was released, which became the definitive reference for the language, as there was not yet an official standard." wiki C++ History So it didn't change between C++11 and C++14. I can assume (and please take this with a grain of salt) it changed between "pre-standardization" and standardization. Maybe someone who knows better the history of C++ can shed more light here.

As for what actually happens:


First let's get out of the way the simple one:

extern g(double);

This is invalid C++. Historically, unfortunately C allowed omission of type. In C++ you have to write extern void g(double).


Next, let's ignore the g(double) overload to answer your first question:

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

int main()
{
    f(2);
}

In C++ there is the infamous two phase name lookup:

  • In the first phase, at the template definition, all non-dependent names are resolved. Failure to do so is a hard error;
  • Dependent names are resolved in phase two, at the template instantiation.

The rules are a bit more complicated, but that is the gist of it.

g is dependent on template parameter T so it passes the first phase. That means that if you never instantiate f, the code compiles just fine. At the second phase f is instantiated with T = int. g(int) is now searched, but not found:

17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup
g(value);
^
24 : note: in instantiation of function template specialization 'f<int>' requested here
f(2);
^
20 : note: 'g' should be declared prior to the call site
void g(int v);

In order for an arbitrary name g to pass with flying colors we have a few options:

  1. Declare g previously:
void g(int);

template <class T>
void f(T value)
{
    g(value);
}
  1. bring g in with T:
template <class T>
void f(T)
{
    T::g();
}

struct X {
   static void g();
};

int main()
{
    X x;
    f(x);
}
  1. Bring g in with T via ADL:
template <class T>
void f(T value)
{
    g(value);
}

struct X {};

void g(X);

int main()
{
    X x;
    f(x);
}

These of course change the semantics of the program. They are meant to illustrate what you can and cannot have in a template.


As for why doesn't ADL find g(int), but finds g(X):

§ 3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

  1. For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered [...]:

    • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

    • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members. [...]


And finally we get to why extern void g(double); inside main is not found: first of all we showed that g(fundamental_type) is found iff it is declared prior to the f definition. So let's make it void g(X) inside main. Does ADL find it?

template <class T>
void f(T value)
{
    g(value);
}

struct X{};


int main()
{
  X x;
  void g(X);

  f(x);
}

No. Because it does not reside in the same namespace as X (i.e. global namespace) ADL can't find it.

Proof that g is not in global

int main()
{
  void g(X);

  X x;
  g(x); // OK
  ::g(x); // ERROR
}

34 : error: no member named 'g' in the global namespace; did you mean simply 'g'?

like image 160
bolov Avatar answered Mar 12 '23 03:03

bolov