检测c++ lambda是否可以转换为函数指针
Detect if C++ lambda can be converted to function pointer
我有一些代码为我正在研究的JIT想法生成程序集。我使用元编程来生成调用,方法是分析函数类型,然后生成正确的程序集来调用它。我最近想添加lambda支持,lambdas有两个版本,非捕获(普通的__cdecl函数调用)和捕获(__thiscall,以lambda对象作为上下文的成员函数调用)。
__thiscall稍微昂贵一些,所以我希望尽可能避免它,并且我也希望避免必须根据lambda类型使用不同的调用生成函数。
我尝试了许多方法来检测lambda类型通过模板和SFINAE和所有失败。
非捕获lambda有一个::operator function_type*
,可以用来将它们转换为函数指针,而捕获lambda没有。
相关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 contextn", function);
}
template <class C>
void lambda_pointer_ctx(C lambda) {
int(C::*function)() const = &C::operator();
void* context = λ
printf("Lambda function: %p with context: %pn", 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;
}
如果您知道要将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, "!");
这样它也可以与泛型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> {};
测试: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, "!");
}
GCC , vc++ , 叮当声+ +
非捕获lambda有一个非常有趣的属性:它们可以转换为适当的函数指针,但是当您对它们应用一元operator +
时,它们也可以隐式地这样做。因此:
template<class...> using void_t = void;
template <class T, class = void>
struct has_capture : std::true_type {};
template <class T>
struct has_capture<T, void_t<decltype(+std::declval<T>())>> : std::false_type {};
int main() {
auto f1 = []{};
auto f2 = [&f1]{};
static_assert(!has_capture<decltype(f1)>{}, "");
static_assert( has_capture<decltype(f2)>{}, "");
}
您正在使用的HasConversion
方法是从c++ 03延续下来的。这里的想法是使用test
重载的不同返回类型(例如,一个返回char
,另一个返回long
),并检查返回类型的sizeof()
是否与您期望的匹配。
void_t
:
template <typename... >
using void_t = void;
写一个类型特征:
template <typename T, typename = void>
struct has_operator_plus : std::false_type { };
template <typename T>
struct has_operator_plus<T,
void_t<decltype(+std::declval<T>())>>
: std::true_type { };
int main() {
auto x = []{ return 5; };
auto y = [x]{ return x(); };
std::cout << has_operator_plus<decltype(x)>::value << std::endl; // 1
std::cout << has_operator_plus<decltype(y)>::value << std::endl; // 0
}
- 转换函数,将 std::数组的双精度作为参数或双精度作为参数单独转换
- C++:用户定义的显式类型转换函数错误
- 为什么下面带有非常量转换函数的代码没有歧义?
- 为什么转换函数声明不需要至少一个定义类型说明符
- PCL:当我在setConditionFunction中使用std::bind 时,没有合适的转换函数
- C++ 通过自定义赋值运算符隐式转换函数参数
- 当我使用 void 函数的返回值(通过强制转换函数指针)时,究竟会发生什么?
- 在C++中自动向下转换函数参数
- static_cast:转换函数模板——它们真的有效吗
- 通过像printf这样的可变参数函数传递一个带有常量字符*转换函数的类
- 隐式转换函数的返回对象时是否会影响性能?
- C++不存在合适的转换函数
- 为什么允许 int 和 const int 使用不同的转换函数?
- 使用转换函数直接初始化
- 为什么用户定义的转换函数模板不能有推导的返回类型?
- 为什么我会收到转换函数错误
- 选择用于赋值初始化的转换函数的优先级
- 我收到错误:没有合适的转换函数从 std::basic_istream<char、std::char_traits<char>> 到 char 存在
- 具有转换函数的 lambda,指向具有 C++ 链接的函数的指针
- 使用模板和部分专用化生成类型转换函数