3

I'm trying to cast a parameter-less generic lambda to a function pointer. This problem generally applies to generic lambdas, which parameters don't depend on the template argument.

Example tries to cast the lambda with one parameter (int) which is typewise independent of the template parameter T.

#include <iostream>

int main()
{
    auto lambda = []<class T>(int v) -> void
    {
        std::cout << typeid(T).name() << ' ' << static_cast<T>(v) << '\n';
    };

    // How do I cast lambda to a function pointer that uses operator(int)<char>
    // This doesn't compile, I've reached this conclusion after examining this page https://en.cppreference.com/w/cpp/language/lambda
    auto seeked_ptr_char = lambda.operator fptr_t<char>();

    // Hacky solution :(
    auto mptr_char = &decltype(lambda)::operator()<char>;
    decltype(mptr_char) mptr_float = &decltype(lambda)::operator()<float>;

    (lambda.*mptr_char)(48);
    (lambda.*mptr_float)(52);

    // This is okay (tho parameter is dependent on a template argument, which is not what we are looking for)
    auto another_lambda = []<class T>(T v) -> void
    {
        std::cout << v << '\n';
    };

    void(*ptr_char)(char) = another_lambda;
    ptr_char(50);
 
    return 0;
}

Doesn't compile with x86-64 gcc 12.1 with -std=c++20 -O3

x86-64 clang 14.0.0 -std=c++20 -O3

https://godbolt.org/z/cavbT5jM3

13
  • "According to original proposal and cppreference this should work?" According to whom? Commented Jul 29, 2022 at 1:56
  • There's something very important that's missing from your question: a question. I searched far and wide, and I completely failed to uncover anything other than a general description of your current task. Ok, and? What's the question? The shown code doesn't compile with some error? Compiles and runs fine for me, with gcc 12 and -std=c++20. I have no idea what the question is, here. Commented Jul 29, 2022 at 1:58
  • @SamVarshavchik I've updated the code snippet, it should be clearer now. Commented Jul 29, 2022 at 2:04
  • @NicolBolas According to wasn't a proper wording here. I've meant the conclusion I've reached by examining cppreference and original proposal. Commented Jul 29, 2022 at 2:06
  • 2
    This does not compile on clang or gcc. I've posted a link to godbolt in the question. Commented Jul 29, 2022 at 2:16

2 Answers 2

7

How do I cast lambda to a function pointer that uses operator(int)<char>

TL;DR: you don't.

First, function pointers point to functions. Templates are not functions yet; a function template only becomes a function when you supply it with template parameters. A function pointer can point to a specific instantiation of a function template. But it cannot point to a function template.

The conversion from a generic lambda to a function pointer relies on invoking a template conversion functions. As such, the lambda effectively has a conversion function like this:

using func = void(int);

template<typename T>
operator func*();

However, that's a template conversion function. And since the type being converted to does not supply the template parameter, in order to call that function, you must explicitly provide that type. Which means you have to explicitly call the conversion function.

But given the code you wrote, you worked all of that out. Which means your real problem is:

How do I explicitly invoke a template conversion function if the result of the conversion is in no way related to the template parameter(s)?

Lambdas don't matter here. This is all about calling a special kind of template conversion function.

And the answer is... apparently, you don't.

You can explicitly invoke a conversion function to the type type_name via object_name.operator type_name() syntax. The problem is this line in the standard:

The conversion-type-id in a conversion-function-id is the longest sequence of tokens that could possibly form a conversion-type-id.

The problem is how lambda.operator fptr_t<char>() gets parsed. See, fptr_t could be the name of a template class. And therefore, fptr_t<char> could be the name of a specialization of that class template. Therefore fptr_t<char> is "the longest sequence of tokens that could possibly form a conversion-type-id".

In short, the compiler thinks you're trying to invoke the conversion function to the type fptr_t<char>. It does not matter that fptr_t is not a template, and the compiler can look around and figure that out. Parsing happens before any of that kind of thinking. So the parsing rules take priority. The entire fptr_t<char> text is taken as the typename for the conversion function.

This all means that, unless the template parameter(s) for a conversion function can be deduced from the type being converted to alone, it appears that it is impossible to call such a conversion function. Such a conversion function can exist; you can declare such functions just fine. But C++ lacks any syntax for actually interacting with it.

Yet another reason why lambdas with template parameters that cannot be deduced from the function arguments are not especially useful.

Sign up to request clarification or add additional context in comments.

Comments

0

Thanks for @Nicol pointing it out, my solution is not a function pointer. It needs more edition..


You can convert it to a std::function through another lambda function as a wrapper, I have no idea how it can be further converted to a function pointer though,

auto wrapper = []<typename T, typename F>(F&& f) {
    return [f = std::forward<F>(f)](auto&&... args) {
        f.template operator()<T>(std::forward<decltype(args)>(args)...);
    };
};

std::function<void(int)> ptr_char = wrapper.operator()<char>(lambda);

ptr_char(48);

Demo

4 Comments

He asked how to convert it to a function pointer, not how to call it.
I thought since the return type is a non-templated lambda, it can be converted to a function pointer..?
But the inner lambda has a capture and therefore cannot be converted to a function pointer.
You're right. Thanks for pointing it out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.