将 lambda 作为模板参数传递:实际推导出什么类型
Passing lambdas as template parameters: what type is actually deduced?
如果我传递一个lambda作为模板参数,推导出的该参数的实际类型是什么?我已经查看了VS2017调试器和此lambda的类型:[](int x) {return x; }
filename::__I2::int<lambda>(int)
。
我问这个问题的原因是因为我想传递一个 lambda,然后从中创建内部std::function
。请注意,这与这个答案有关,以及为什么我们必须使用 CTAD 来构造内部std::function
,而不仅仅是将模板参数传递给std::function
。
例如,我想做如下事情:
template<class Func, class... Args>
void createStdFunc(Func f, Args... args) {
std::function<Func> internalFunc = f; //this does not work
}
//usage
createStdFunc([](int x) {return x; }, 5);
但是,这不起作用,并且我收到错误'initialising' cannot convert from 'Func' to 'std::function<Func>'
.我不确定这些类型有何不同以及它们如何从传递到函数到初始化std::function
。请注意,我知道您可以从 2017 年开始使用 CTAD,但想知道 2014 年及之前的解决方案是什么?
在 C++14 中,您可以使用返回类型推导来计算函数签名,这意味着传递到createStdFunc
的参数类型匹配:
template<class Func, class... Args>
void createStdFunc(Func f, Args... args) {
std::function<std::result_of_t<Func(Args...)> (Args...)> internalFunc{f}; //this does work
}
我的方式
#include <iostream>
#include <functional>
template <typename R, typename T, typename ... As>
constexpr std::function<R(As...)> getFuncType (R(T::*)(As...) const);
template <typename F, typename ... As>
void createStdFunc (F const & f, As ... as)
{
decltype(getFuncType(&F::operator())) internalFunc { f };
internalFunc(as...);
}
int main ()
{
createStdFunc([](int x) { std::cout << x << std::endl; }, 5);
}
也许也通过using
template <typename F>
using funcType = decltype(getFuncType(&F::operator()));
template <typename F, typename ... As>
void createStdFunc (F const & f, As ... as)
{
funcType<F> internalFunc { f };
internalFunc(as...);
}
代码内部的问题在于Func
不是函数类型。这是 lambda 的类型。Lambda 编译成这样:
// equivalent:
// auto my_lambda = [](int v){ return v; };
struct /* unnamed */ {
auto operator()(int v) const { return v; }
} my_lambda;
解决方案是从闭包类型中提取operator()
的类型:
using my_lambda_t = decltype(my_lambda);
// type: int(my_lambda_t::*)(int) const;
auto call_operator = &decltype(my_lambda_t)::operator();
然后,从 operator()
的类型中,可以推导出参数的类型和返回类型:
template<typename>
struct extract_types {};
template<typename R, typename C, typename... Args>
struct extract_types<R(C::*)(Args...) const> {
using result = R;
using args_types = std::tuple<Args...>;
};
此模式的通用版本在 Boost.CallableTraits 中提供
您可以编写一个简单的特征来泛化可调用类型。如果您同时处理函数指针和任何具有operator()
(const
和非const
(的内容,您应该能够涵盖大多数用例。
#include <tuple>
// For callable types
template<class T>
struct func_type : func_type<decltype(&T::operator())>{};
// For callable types' member functions (including `operator()`)
template<class T, class R, class ... Args >
struct func_type<R (T::*)(Args...) const> : func_type<R(*)(Args...)> {};
// For function pointers
template<class R, class ... Args >
struct func_type<R (*)(Args...)> {
using type = R(Args...);
using result = R;
using args = std::tuple<Args...>;
};
template<class T>
using func_type_t = typename func_type<T>::type;
然后,func_type_t<T>
应该为您提供大多数可调用类型的函数类型T
。示例用途:
#include <functional>
template<class Func, class... Args>
void createStdFunc(Func f, Args... args) {
// Replaced `Func` with `func_type_t<Func>`
std::function<func_type_t<Func>> internalFunc = f;
}
int foo(int x) { return x; }
struct bar {
int operator()(int x) { return x; };
};
int main()
{
// With lambda expression
createStdFunc([](int x) {return x; }, 5);
// With function pointer
createStdFunc(foo, 5);
// With std::function
std::function<int(int)> std_func = [](int x) {return x; };
createStdFunc(std_func, 5);
// With a functor
createStdFunc(bar{}, 5);
}
std::function
模板期望一个函数类型作为其参数,从中推断可调用对象包装的返回和参数类型。lambda 表达式的闭包类型是可调用的,但它不是函数类型。
C++17 引入了std::function
的演绎指南,允许从任何可调用的参数中推断出正确的类型。在 C++17 之前,您可以使用一组帮助程序模板来推断正确的类型,例如:
template <typename F>
struct deduce_func_type_helper;
template <typename R, typename... Args>
struct deduce_func_type_helper<R(&)(Args...)>
{
using type = std::function<R(Args...)>;
};
template <typename R, typename... Args>
struct deduce_func_type_helper<R(*)(Args...)> : deduce_func_type_helper<R(&)(Args...)> {};
template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...)> : deduce_func_type_helper<R(&)(Args...)> {};
template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...) const> : deduce_func_type_helper<R(&)(Args...)> {};
template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...) volatile> : deduce_func_type_helper<R(&)(Args...)> {};
template <typename F>
struct deduce_func_type_helper<F&> : deduce_func_type_helper<std::remove_cv_t<F>> {};
template <typename F>
struct deduce_func_type_helper<F&&> : deduce_func_type_helper<std::remove_cv_t<F>> {};
template <typename F>
struct deduce_func_type_helper : deduce_func_type_helper<decltype(&F::operator())> {};
template <typename F>
using func_type_t = typename deduce_func_type_helper<F>::type;
现场示例在这里
请注意,上面的例子并不完整;它缺少一些特化,例如,对于const
、volatile
和不同 ref 限定符的所有可能组合。所以这可能会变得非常冗长,如果可以的话,你可能想用 C++17
- 我应该使用什么来代替void作为变体中的替代类型之一
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 数据类型"struct seq<0, 1, 2>{}"含义是什么?
- 在自定义 std::vector-like 容器中处理指针和非指针模板类型的最佳方法是什么?
- 您应该在什么时候创建自己的异常类型
- C++ 未初始化的本地(非全局)int 数组中的元素类型到底是什么?
- 未命名的非类型模板参数有什么意义?
- 静态自动 constexpr t = { "red" , "black" , "green" } 是什么类型;派生到?
- 模板<>模板<类型名 T> 语法有什么用?
- 指针类型类成员的动态强制转换的恒定性是什么?
- 当 c++ 需要一种数据类型并获取另一种数据类型时会发生什么?
- 这是什么类型的C++语法,我应该采取什么步骤来理解这一点
- 返回类型在 C++ OOP 中是什么意思
- 如何声明一个标准::提升直方图的向量?提升直方图的类型是什么?
- 类名后跟括号的类型是什么
- 删除对象(具有不同类型)的引用时会发生什么情况?
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 当你只使用 return 时,函数返回什么类型;在 c++ 中