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:
foo
not virtualChild
not inherit from Parent
baz
baz
not virtualbaz
with the declaration in the headerIs 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.
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.
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.
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).
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.
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:
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:
See these questions for more information:
So:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With