Is There a declval for Function Pointers?
Asked Answered
K

1

1

I have a function and I need to test whether I can pass an argument of a given type to it. For example:

template<typename T, auto F>
decltype(F(declval<T>{})) foo();

Calling foo<int, bar>() does 2 things:

  1. Sets the return type of foo would have the same return type as bar
  2. Ensures that bar is a function that accepts an argument of type T

Unfortunately I don't have access to auto template types, but I still want to accomplish both of these. What I need is a decltype for function pointers, which would allow me to do something like this:

template <typename T, typename F>
decltype(declval<F>(declval<T>{})) foo();

So I could still call foo<int, bar>() and get the same result. Of course there isn't a declval for function pointers. But is there another way I could accomplish this?

Katlin answered 1/3, 2019 at 15:11 Comment(9)
declval works on any type, including function pointers.Mixie
Would you be okay with foo<int, decltype(&bar), bar>()?Baobaobab
... or std::functions :/Olivarez
@Baobaobab that's what I have right now. That was what we did before auto I guess... but not my desired solution.Katlin
@Olivarez I'm not certain how I could use a std::function to work around this?Katlin
Do you need the actual function pointer passed to the function? Does the pointer need to be known at compile time?Baobaobab
@Baobaobab Nope, I'm fine with the struck through code in my question. I just need to match the return type of bar. Using auto just let me call decltype on F.Katlin
Okay. Did the second example fail to compile for you, as it should work? declval works on all types as pointed out in Guillaume Racicot's answer.Baobaobab
@Baobaobab I was missing the constructor on declvalKatlin
C
3

Of course there isn't a declval for function pointers.

What do you mean? std::declval works perfectly with function pointer types:

template<typename F, typename... Args>
using call_t = decltype(std::declval<F>()(std::declval<Args>()...));

In this example, F can be a function pointer type, a lambda type or any callable types.

Here's an example of usage:

template<typename T, typename F>
auto foo() -> call_t<F, T>;

Another example using the detection idiom (implementable in C++11):

template<typename F, typename... Args>
using is_callable = is_detected<call_t, F, Args...>;

static_assert(is_callable<void(*)(int), int>::value, "callable")

Note that all this can be replaced by std::invoke_result_t and std::is_invocable in C++17. I'd suggest mimicking those to have the most seamless upgrade.

Callicrates answered 1/3, 2019 at 15:23 Comment(7)
The problem with this is the OP doesn't have an actual function pointer to use in the function. They still have to get that in there somehow.Baobaobab
If I look at it's two requirements, he don't need the actual function pointer valueCallicrates
Looks like you're right. Not sure why the OP thinks it doesn't work.Baobaobab
@Baobaobab This has my +1 it does work just not for me in Visual Studio, but I tested in g++ and that does work. I'll post a follow up question... Also if no one can give me a cross platform solution I will accept this.Katlin
@JonathanMee can you create a compiler explorer example that reproduce your error? This code should be cross platform.Callicrates
@Baobaobab So my example is a little more involved. I think the problem is nesting using statements on VisualStudio: https://mcmap.net/q/1483331/-templated-usings-can-39-t-be-nested-in-visual-studio/2642059Katlin
@GuillaumeRacicot You're right this is an excellent answer and should be cross platform... but for VisualStudio bugs.Katlin

© 2022 - 2024 — McMap. All rights reserved.