Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Implicit instantiation of member function templates

Tags:

c++

templates

I would like to have a better understanding of when the compiler will implicitly instantiate a member function template.

Consider the following example:

// example.h

struct Parent {
  virtual void foo(double) {}
};

struct Child : Parent {
  void foo(double d) { bar(d); }

  template<typename T> void bar(T);

  virtual void baz();
}; 


// example.cpp
#include "example.h"
template <typename T> 
void Child::bar(T) {}

void Child::baz() {}

Compiling this with [g++|clang++] -c example.cpp, both GCC and clang implicitly instantiate the function template Child::bar<double>. However, the following seemingly minor changes prevent this:

  • Making foo not virtual
  • Making Child not inherit from Parent
  • Removing baz
  • Making baz not virtual
  • Defining baz with the declaration in the header

Is there any reasonably concise explanation of when implicit instantiation takes place, or do I need to wade through a copy of the standard? I've searched SO for other questions relating to implicit instantiation, but didn't find much. The closest I found was this question, but it's about virtual functions in class templates (not member function templates).

To be clear: I understand that this issue can be avoided by either including the definition in the header or explicitly instantiating the templates that I need. This merely arose as a point of curiosity when digging into why one class (whose explicit instantiations I had inadvertently omitted) nonetheless happily compiled and linked.

like image 479
Tom Avatar asked Jan 23 '15 16:01

Tom


People also ask

How the instantiation of function template happens?

When a function template is first called for each type, the compiler creates an instantiation. Each instantiation is a version of the templated function specialized for the type. This instantiation will be called every time the function is used for the type.

What is implicit instantiation?

Implicit instantiation means that the compiler automatically generates the concrete function or class for the provided template arguments. In general, the compiler also deduces the template arguments from the function's arguments. In C++17, the compiler can also deduce the template arguments for class templates.

Is it necessary to instantiate a 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).

What is the instantiation of the class template?

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.


1 Answers

I've put your code into Visual Studio 2013 (child.h and child.cpp)and tried the following:

#include "child.h"
Child c1;
c1.bar(10.2);

This produce an unresolved external error which indicate no "implicit instantiation" happens. indicating there is a clear difference between Visual Studio and G++ in this case. This was fixed by moving the code of Child::foo into the child.cpp file.

Therefore the short answer is: What you are experiencing is to a large degree compiler specific and for portability u should not rely on this behavior. By C++ standards the safest is to either put your template definitions in a .h (or .hpp) file or explicitly instantiate the templates as required. Any other way of managing this will likely break in some compiler.

To further understand the behavior of Visual Studio here take a look at this:

  • https://msdn.microsoft.com/en-us/library/aa299373%28v=vs.60%29.aspx
  • https://isocpp.org/wiki/faq/inline-functions#inline-member-fns

What is says is that any function defined in the definition of the class is implicitly inline. The Compiler can delay the instantiation of an inline function if it so chooses. This means when compiling child.obj Child::foo(d) is never created which in turns would mean bar is never instantiated so this cause the compile problem during the linking stage. Why visual Studio can actually do this considering foo(double) is technically a virtual function is indeed strange, but it seems Visual Studio leaves the instantiation of foo() for later when Child is used. So for example:

Parent *p1 = new Child();

Also causes a problem as at this time the compiler tries to create Child::foo(double) and can't because of the missing template definition.

From your results the assumption is that GCC will immediately instantiate inline functions if they are virtual.

The behavior you are experiencing is a combination of things:

  1. How compilers handle implicit inline functions
  2. How virtual tables are created for virtual functions
  3. And when the compiler is required to instantiate the virtual member functions.

See these questions for more information:

  • When is a vtable created in C++?
  • Is "inline" implicit in C++ member functions defined in class definition

So:

  • It seems the compiler are allowed to delay instantiating of inline functions until used and then decide if it wants to "inline" them or create a separate function.
  • Until the time a class is used the compiler is not required to complete the instantiation of everything required for the object
  • In fact under optimization even when creating an object the compiler might not have everything defined in code for the class ready.

I've decided to not answer your specific queries in detail as my research seem to indicate the fact that your code work is circumstantial and this behavior is not something that should be relied on.

like image 142
Heinrich du Toit Avatar answered Sep 18 '22 12:09

Heinrich du Toit