Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How template explicit instantiation works and when?

Here is an exercise from C++ primer 5th edition:

"Exercise 16.26: Assuming NoDefault is a class that does not have a default constructor, can we explicitly instantiate vector<NoDefault>? If not, why not?"

Here is my guess:

Yes we can instantiate it:

template <typename T>
class Foo
{
public:
    void func(){cout << x_.value_ << endl;}
private:
    T x_;
};

class Bar
{
public:
    Bar(int x) : value_(x){}
    void print(){}
private:
    int value_{};
template <class T>
friend class Foo;
};

extern template class Foo<Bar>; // instantiation declaration
template class  Foo<Bar>; // instantiation definition


int main()
{

  //  Foo<Bar> f;
}

The code works fine but if I uncomment the line in main I get error as expected because Bar is not default-constructible.

If I use the same class Bar as an element type for std::vector it doesn't work:

extern template class vector<Bar>; // declaration ok
template class vector<Bar>; // instantiation: doesn't work?!
  • So why my Foo<Bar> instantiation works but not vector<Bar>?

  • What looks to me is that it is logical in vector<Bar> not to work because an explicit instantiation definition instantiates all the members (even the ones not used) of the class template; and in this example among them the default constructor of Foo<Bar> that implies a default ctor of its element type Bar; the latter doesn't provide one; after all Foo<Bar>() normally is declared as a deleted member function because x_ doesn't have a default constructor. SO I don't know why Foo<Bar> definition works?! Thank you.

like image 445
Maestro Avatar asked Nov 12 '20 21:11

Maestro


People also ask

What is explicit instantiation and how to use it?

Privacy policy. Thank you. You can use explicit instantiation to create an instantiation of a templated class or function without actually using it in your code. Because this is useful when you are creating library (.lib) files that use templates for distribution, uninstantiated template definitions are not put into object (.obj) files.

Should template declaration include explicit instantiation?

I.e, in order to reap the compile/link-time benefits of explicit instantiation, one must only include the template declaration so that the compiler cannot instantiate it? @user123456: Probably compiler dependent. But more than likely true in most situations.

How do I manually instantiate a template in C++?

This happens because the C++ compilation system automatically generates those instantiations as they are needed. The C++ standard also offers a construct to instantiate templates manually: the explicit instantiation directive . To illustrate manual instantiation, let's revisit our original example that leads to a linker error (see page 62).

How to prevent the automatic instantiation of members of a template?

You can explicitly instantiate function templates by using a specific type argument to re-declare them, as shown in the example in Function Template Instantiation. You can use the extern keyword to prevent the automatic instantiation of members. For example: Similarly, you can mark specific members as being external and not instantiated:


1 Answers

In the Standard, the [temp.explicit] section explains what happens in an explicit instantiation. In particular, p12 provides that:

An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.

Now, std::vector<T> has a constructor that takes an integer n and initializes the vector with n value-initialized T's. It can be assumed that the definition of this constructor is somewhere inside the <vector> header (see Why can templates only be implemented in the header file?). So the explicit instantiation definition of std::vector<Bar> will instantiate that constructor with T = Bar.

Because this is an explicit instantiation, it is not only the signature of that constructor that is instantiated, but its entire body as well. This must, somewhere, include a call to the default constructor of Bar (possibly as part of another function that it calls, which would also be instantiated at this point), so a compilation error occurs as part of the explicit instantiation definition of std::vector<Bar>. Note that if you were implicitly instantiating std::vector<Bar>, it would only instantiate (roughly speaking) the signatures of the member functions. This is why it's legal to actually define and use std::vector<Bar> objects, as long as you don't call any function that requires the default constructor of Bar to exist.

The reason why an explicit instantiation definition of Foo<Bar> succeeds is that, when Foo<Bar> is instantiated, the compiler marks its default constructor as deleted (this always happens whenever there is a non-default-constructible non-static member). It therefore does not at any point attempt to compile any code that requires the default constructor of Bar, and no error occurs.

like image 116
Brian Bi Avatar answered Oct 23 '22 04:10

Brian Bi