How to Explicitly Instantiate a Template Function

Explicit instantiation of a deleted function template in C++

Clang and GCC are right. MSVC is probably referring to the following rule ([dcl.fct.def.delete]/2):

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

An explicit instantiation definition is a declaration, so it is allowed.

Although, to be fair, it's not clear what "refers" means in this context, so there is some room for language lawyering. But it's clear that, in general, the mere instantiation of a template to produce a deleted function definition is allowed. [temp.inst]/3.2 also mentions the implicit instantiation of deleted member functions that occurs when class templates are implicitly instantiated. If instantiating a templated entity to produce a deleted function definition were ill-formed, it wouldn't be possible to use class templates like std::atomic that have a deleted copy constructor. Your program merely does explicitly what happens implicitly under those circumstances.

Automating explicit template instantiation

If the compiler sees that code isn't referred to, even for static initialization with side effects, it can eliminate it, and I think that's the case in your example. It can "prove" those class instantiations are not used, and so the side effects are lost.

For a non-standard solution, but one that works on g++ (and presumably clang, but not tested) is to mark your static data members with the "used" attribute:

template <Enum Value>
struct Instantiate
{
using Function = int (Type::*)() const;
static constexpr Function f1 __attribute__((used)) = &Type::get<Value>;

// many more member-functions
};

Update

Reviewing the standard, the wording seems like I got it exactly backwards:

"If an object of static storage duration has initialization or a
destructor with side effects, it shall not be eliminated even if it
appears to be unused, except that a class object or its copy may be
eliminated as specified in ..."

So I've had this in my head for decades, and now I'm uncertain as to what I was thinking. :) But it seems related, given the attribute helps. But now I have to learn what's going on.

How template explicit instantiation works and when?

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.

explicit instantiation with default template/function arguments

The problem is that you did not keep the signature consistent. The declaration in the header accepts by rvalue reference, the implementation file by value, and the instantiation is for a function with absolutely no parameters (a default argument doesn't mean a function has no parameters).

You need to stick to the same signature everywhere.

So either

#include <functional>

template<typename T = std::function<void(int,int)>> void foo (T &&t = [](int,int)->void{});
//in .cpp
template<typename T> void foo (T&&){}

template void foo<>(std::function<void(int,int)>&&);

Or

#include <functional>

template<typename T = std::function<void(int,int)>> void foo (T t = [](int,int)->void{});
//in .cpp
template<typename T> void foo (T){}

template void foo<>(std::function<void(int,int)>);

Explicit instantiation of template class with templated member functions

The ambiguity is not coming from anything to do with template instantiation of the class, it's caused by Eval also being a templated function.

&A<int>::Eval does not point to a function, it points to a template. And there is just no such type as a "pointer to a template".

If you want a pointer to A<int>::Eval, you need to specify D as well.

auto eval_ptr = &A<int>::Eval<int>; works just fine for example.

Addendum: Pointers-to-templates do exist in the grammatical sense, but there is no type an object can have to hold one of them. They must be immediately casted/decayed to a specific overload in order to be used, which doesn't come into play here since you want to store it in an auto.

For example: The following is fine because there's clearly only one "version" of Eval that can be meant:

void bar(void (A<int>::*arg)(int&)) {}

void foo() {
bar(&A<int>::Eval);
}

When do we need to explicitly instantiate a template function?

If you are writing a library then the templates not invoked by the library's code will not be implemented, therefore the library may be missing some functions that you intended to provide.
The explicit instantiation will force the compiler to create an implementation for the specified template, even if no calls to it have been made.

When you finally link your library with the client application then the linker will find the implementation for the types that your library supports.

Maybe an explanation from a native English speaker will be clearer: https://docs.microsoft.com/en-us/cpp/cpp/explicit-instantiation



Related Topics



Leave a reply



Submit