Is there a way to deduce the value of a function pointer template parameter?
Asked Answered
C

1

7

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.

Changchangaris answered 19/7, 2013 at 0:41 Comment(6)
How could it deduce it? For example, I could do this: template<std::size_t N> void foo(std::size_t i) {int arr[N]; /*fill*/ return arr[i];}. I would definitely want an error if I happened to forget the template argument when this deduction could be done.Lingo
Come to think of it, I'm not sure I understand why it needs to be passed as a function argument as well. Isn't templ<func>(); enough?Lingo
@chris: Right, my question didn't make much sense as written :) Please see my edit.Changchangaris
I don't think this is possible. There was a proposal to be able to deduce T in template<using typename T, T t>, but I don't think it made it in.Lingo
If you are happy passing the function as an argument, why do you also want that same function as a template parameter? What's wrong with template <typename Function> templ(Function func); ?Fogarty
@IgorTandetnik: To allow the compiler to perform better optimizations by generating a different instantiation for each function passed as an argument (not just each type of function), and then perhaps inlining each if appropriate.Changchangaris
G
3

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 it A), 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 make P, after substitution of the deduced values (call it the deduced A), compatible with A.

A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A 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 a T, and () represents argument lists where no parameter contains a T. Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i.

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

Gemmell answered 23/7, 2013 at 10:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.