I would like to declare a function per typename, so that I can refer to it by type. Critically, I would like to do this even for nameless lambda types. The classic way to do this is of course a template:
template <typename T>
void template_foo() {}
Now I can call this function by using the type of a function:
auto my_lambda = [] {};
template_foo<decltype(my_lambda)>();
and the compiler instantiates a template_foo symbol for each type passed as a template parameter.
However, I want to build a reflection-like tool (based on LLVM) that will define template_foo<T> for all T that is used in the program. Since decltype(my_lambda) can't be named, it can't generate a header that declares an extern template instantiation, and anyway in principle that should not be required -- the compiler ought to be able to refer to the mangled symbol without instantiating it. How can I prevent the compiler from instantiating this function for all T so I can instantiate it myself in a different TU?
Note that a related thing is possible with template variables:
template <typename T>
struct Foo {
static int my_int;
}
When Foo<decltype(my_lambda)>::my_int is used, the compiler is forced to assume it is defined in some other translation unit (recent versions of Clang warn about this but as far as I can tell, the warning is because this is seldom what users want, not because it is illegal).
However, there is no equivalent mechanism for specifying a specialized function.
Here's a complete MVCE that demonstrates what I want to do:
#include <cstdint>
#include <cstdio>
template <typename T>
struct Foo {
static int my_int;
};
template <typename T>
int my_func();
int main() {
// no error here, but can't work due to below error
printf("%i", my_func<int>());
// works - code emitted, but does not link without a definition
printf("%i", Foo<int>::my_int);
}
// ERROR: specialization of 'int my_func() [with T = int]' after instantiation
template <>
int my_func<int>() {
return 42;
}
How can I prevent the compiler from instantiating any versions of a template?
Option A:
friendnon-template functionsTemplates have the limitation that you cannot specialize them after they were instantiated. With
friends however, we can generate non-template functions within a class template and call those:This exploits the fact that
int call(tag<T>)is not a function template, but a concrete function that takes atag<T>as an argument, such astag<int>.With partial specializations of the
tagclass, we can also decide to definecallfor someT:Option B: use second template function
If we don't need to partially specialize, we could just as well use another function template to solve this issues:
The compiler will emit
call my_func<int>()andcall my_func<float>(). We deal with the fact that we have some specializations in the second file, wheremy_funcisn't specialized, onlymy_func_implis.Lambda Expressions
As for lambda expressions: you obviously run into a problem with linking a call
call(tag<decltype(some_lambda)>)between header and source file. If you want to call this function in the header and define it in the source, you will need to refer to the exact same lambda type, meaning you need a type alias for it: