Home > Enterprise >  Can we write a function which returns a function pointer to a function template?
Can we write a function which returns a function pointer to a function template?

Time:04-12

Is it possible to write a function or method which can return a pointer to a template function or template method?

Example:

#include <iostream>
struct X1 {
  static void Do(auto n) { std::cout << "1" << n << std::endl; }
  // static auto GetPtr() { return X1::Do; }  // how to write such a function?
};
struct X2 {
  static void Do(int n) { std::cout << "2" << n << std::endl; }
  //static auto  GetPtr(){ return &Do; }
};

template <typename T> T magic(bool b, T t1, T t2) { return b ? t1 : t2; }

int main() {
  auto l1 = magic(true, X1::Do, X2::Do);

  // should be replaced by:
  //   auto l1 = magic( true, X1::GetPtr(), X2::GetPtr() );

  l1(100);
}

If I compile the above out-commented functions, I got from gcc:

main.cpp:1845:39: error: unable to deduce 'auto' from 'X1::Do'

Background: I am currently trying to understand the overload resolution in same cases. In the given case you see that the overload for int is taken because one function pointer only has an int parameter so the second pointer overload can be found.

I was triggered by that question: Ternary operator applied to different lambdas produces inconsistent results

Here in an answer was suggested, that a lambda should be able to provide a conversion operator to a function pointer... and I did not see it :-)

CodePudding user response:

The compiler doesn't "know" in advance all your uses for X1::GetPtr (generally). It seems you are expecting the compiler to 1. recognize it is a template 2. recognize all uses for the function, and see if it can deduce all instantiations needed for the template "for free", so to speak - in your case only the use in magic, but this is not general.

There is no such mechanism in C and the compiler must know the type of the function when it parses it, or recognize it as a template (and not guess it).

Simply put, I think you are expecting the compiler to do something too difficult, and it can't. As such, you will have to do the template resolution yourself:

template<typename N>
static auto GetPtr() { return &X1::Do<N>; }

and call it with

magic(true, X1::GetPtr<int>(), X2::GetPtr());

CodePudding user response:

No you cannot return a pointer to a function template, because a function template is not a function. It is a template.

// static auto GetPtr() { return X1::Do; }  // how to write such a function?

You need & to get a pointer to a member function, though Do is not a member function it is a member function template. You could return a pointer to X1::Do<int> or to X1::Do<double> but there is no pointer to X1::Do.

You can however return a functor with an overloaded call operator and that operator can be a template:

struct foo {
   template <typename T>
   void operator()(const T& t) {}

   void operator()(int x){}
};    

foo magic() { return foo{}; }

int main() {
    magic()(3);                    // calls operator()(int)
    magic()("hello world");        // calls operator()<const char[12]>
}

After rereading your question and the Q&A you link, I think you are maybe looking for this:

#include <iostream>
struct X1 {
  static void Do(auto n) { std::cout << "1" << n << std::endl; }
  static auto GetPtr() { return &X1::Do<int>; }
};
struct X2 {
  static void Do(int n) { std::cout << "2" << n << std::endl; }
  static auto  GetPtr(){ return &Do; }
};

template <typename T> T magic(bool b, T t1, T t2) { return b ? t1 : t2; }

int main() {
  auto l1 = magic( true, X1::GetPtr(), X2::GetPtr() );

  l1(100);
}

As stated above, you cannot get a member function pointer to X1::Do but you can get a pointer to X1::Do<int>.

And as you are refering to conversion of lambdas to function pointers: Also lambdas with auto argument can only be converted to function pointers after choosing the argument type. Consider the example from cppreference:

void f1(int (*)(int)) {}
void f2(char (*)(int)) {}
void h(int (*)(int)) {}  // #1
void h(char (*)(int)) {} // #2
 
auto glambda = [](auto a) { return a; };
f1(glambda); // OK
f2(glambda); // error: not convertible
h(glambda);  // OK: calls #1 since #2 is not convertible
 
int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK

It is not possible to get a pointer to function of type auto(auto) (it isn't a type of a function to begin with). In all the calls above, after the conversion there is no auto anymore. Instead the requested type is deduced and a conversion to the respective function pointer is done.

  • Related