C++ allows non-type template parameters to be of pointer, including function pointer, type. I recently asked a question about what this is useful for, and this is a follow up to one of the answers.
Is it posible to deduce the value of a function pointer template parameter, from a function argument that is the function pointer in question? For example:
using VoidFunction = void(*)();
template <VoidFunction F>
void templ(VoidFunction);
...
void func(); // a VoidFunction
...
templ<func>(func); // works, but I have to specify the template parameter explicitly
templ(func); // <-- I would like to be able to do this
Is there a way to get this deduction to happen? It seems technically possible from a compiler implementer's point of view, as long as the function argument can be resolved to a function in the code at compile time.
If you're wondering about the motivation behind this, see the comments under this answer, particularly a possible optimization for the implementation of std::bind()
.
EDIT: I realize that I could simply remove the function argument and use the template argument, as in templ<func>()
. My only purpose of adding in the function argument was to try to avoid having to pass the template argument.
I guess what I really want, is to also deduce the type of the function pointer, as in:
template <typename Function, Function F>
void templ(/* something */);
and then be able to call
templ(func);
or
templ<func>();
and have both the type and value be deduced from a single mention of the function pointer.
Hope that makes more sense now.
Template arguments for a function are deduced from the types of the function's template parameters. Template arguments can only be deduced from a type when that type is one of the allowed forms. The allowed forms are specified in [temp.deduct.type]
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it
P
) is compared with an actual type (call itA
), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will makeP
, after substitution of the deduced values (call it the deducedA
), compatible withA
.A template type argument
T
, a template template argumentTT
or a template non-type argumenti
can be deduced ifP
andA
have one of the following forms:
T cv-list T T* T& T[integer-constant] template-name (where template-name refers to a class template) type(*)(T) T(*)() T(*)(T) T type::* type T::* T T::* T (type::*)() type (T::*)() type (type::*)(T) type (T::*)(T) T (type::*)(T) T (T::*)() T (T::*)(T) type[i] template-name<i> (where template-name refers to a class template) TT<T> TT<i> TT<>
where
(T)
represents argument lists where at least one argument type contains aT
, and()
represents argument lists where no parameter contains aT
. Similarly,<T>
represents template argument lists where at least one argument contains aT
,<i>
represents template argument lists where at least one argument contains ani
and<>
represents template argument lists where no argument contains aT
or ani
.
When considering only non-type template arguments, the relevant forms are those that contain i
:
type[i] template-name<i> (where template-name refers to a class template) TT<i>
Therefore it is not possible to deduce the value directly from the value of a function argument that is the function pointer. However it is possible to deduce the value of a non-type template argument if the function parameter has one of the specified forms.
The following code ahieves this by wrapping the non-type template argument value in a class-template called NonType
. The parameter of f
is in the form template-name<i>
, making it possible for the value of its non-type template argument to be deduced.
template<typename T, T value>
struct NonType {};
template<typename T, T value>
void f(NonType<T, value>)
{
}
void g();
struct A
{
void f();
int m;
};
int i;
#define MAKE_NONTYPE(value) NonType<decltype(value), (value)>()
int main()
{
f(MAKE_NONTYPE(0)); // NonType<int, 0>
f(MAKE_NONTYPE(&g)); // NonType<void(*)(), &g>
f(MAKE_NONTYPE(&A::f)); // NonType<void(A::*)(), &A::f>
f(MAKE_NONTYPE(&A::m)); // NonType<int A::*, &A::m>
f(MAKE_NONTYPE(&i)); // NonType<int*, &i>
}
Note that decltype
and the MAKE_NON_TYPE
macro are used here only as a convenience, to avoid having to write out the full template argument list of NonType
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