检测C++ lambda是否可以转换为函数指针

Ste*_* P. 9 c++ lambda templates template-meta-programming c++11

我有一些代码为我正在研究的JIT想法生成程序集.我使用元编程通过分析函数类型生成调用,然后生成正确的程序集来调用它.我最近想添加lambda支持,lambdas有两个版本,非捕获(普通__cdecl函数调用)和捕获(__thiscall,使用lambda对象作为上下文的成员函数调用).

__thiscall稍贵一点,所以我想尽可能避免使用它,我也想避免使用不同的调用生成函数,具体取决于lambda类型.

我尝试了很多方法通过模板和SFINAE检测lambda类型,但都失败了.

非捕获lambda有::operator function_type*一个可以用来将它们转换为函数指针,而捕获lambdas则没有.

相关的C++规范:http://en.cppreference.com/w/cpp/language/lambda

有任何想法吗?

编辑 我想要一个适用于vs 2013/2015,gcc和clang的解决方案

测试代码如下

#include <utility>

    //this doesn't work
    template < class C, class T >
    struct HasConversion {
        static int test(decltype(std::declval<C>().operator T*, bool()) bar) {
            return 1;
        }

        static int test(...) {
            return 0;
        }
    };

    template <class C>
    void lambda_pointer(C lambda) {
        int(*function)() = lambda;

        printf("Lambda function: %p without context\n", function);
    }

    template <class C>
    void lambda_pointer_ctx(C lambda) {
        int(C::*function)() const = &C::operator();

        void* context = &lambda;

        printf("Lambda function: %p with context: %p\n", function, context);
    }

    int main() {
        int a;

        auto l1 = [] {
            return 5;
        };

        auto l2 = [a] {
            return a;
        };


        //non capturing case

        //works as expected
        lambda_pointer(l1);

        //works as expected (ctx is meaningless and not used)
        lambda_pointer_ctx(l1);



        //lambda with capture (needs context)

        //fails as expected
        lambda_pointer(l1);

        //works as expected (ctx is a pointer to class containing the captures)
        lambda_pointer_ctx(l1);

        /*
        //this doesn't work :<
        typedef int afunct() const;

        HasConversion<decltype(l1), afunct>::test(0);
        HasConversion<decltype(l2), afunct>::test(0);
        */


        return 0;
    }
Run Code Online (Sandbox Code Playgroud)

Pio*_*cki 5

如果您知道要将lambda转换为的函数的签名,则可以利用以下std::is_assignable特征:

auto lambda = [] (char, double) -> int { return 0; };
using signature = int(char, double);
static_assert(std::is_assignable<signature*&, decltype(lambda)>::value, "!");
Run Code Online (Sandbox Code Playgroud)

DEMO

这样它也可以用于通用lambdas.


我想要一个适用于vs 2013/2015,gcc和clang的解决方案

如果你不知道签名,这里的方法是检查是否+触发隐式转换的替代方法.这个利用std::is_assignable测试并验证lambda是否可赋值给具有与lambda函数调用运算符相同签名的函数指针.(就像使用一元运算符加上的测试一样,这不适用于通用lambda.但在C++ 11中没有通用的lambdas).

#include <type_traits>

template <typename T>
struct identity { using type = T; };

template <typename...>
using void_t = void;

template <typename F>
struct call_operator;

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};

template <typename F>
using call_operator_t = typename call_operator<F>::type;

template <typename, typename = void_t<>>
struct is_convertible_to_function
    : std::false_type {};

template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
    : std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};
Run Code Online (Sandbox Code Playgroud)

测试:

int main()
{
    auto x = [] { return 5; };
    auto y = [x] { return x(); };

    static_assert(is_convertible_to_function<decltype(x)>::value, "!");
    static_assert(!is_convertible_to_function<decltype(y)>::value, "!");
}
Run Code Online (Sandbox Code Playgroud)

GCC, VC++, Clang ++