提问人:Klaim 提问时间:8/26/2010 最后编辑:Peter MortensenKlaim 更新时间:11/17/2023 访问量:175278
lambda 函数可以模板化吗?
Can lambda functions be templated?
问:
在 C++ 11 中,有没有办法对 lambda 函数进行模板化?还是它本质上太具体而无法模板化?
我知道我可以定义一个经典的模板化类/函子,但问题更像是:该语言是否允许模板化 lambda 函数?
答:
2018 年更新:C++20 将附带模板化和概念化的 lambda。该功能已集成到标准草案中。
2014 年更新:C++14 已于今年发布,现在提供与此示例中相同的语法的多态 lambda。一些主要的编译器已经实现了它。
目前(在 C++ 11 中),可惜没有。多态 lambda 在灵活性和功率方面非常出色。
它们最终成为单态的最初原因是因为概念。概念使这种代码情况变得困难:
template <Constraint T>
void foo(T x)
{
auto bar = [](auto x){}; // imaginary syntax
}
在约束模板中,只能调用其他约束模板。(否则,无法检查约束。可以调用吗?lambda 有什么约束(毕竟它的参数只是一个模板)?foo
bar(x)
Concepts 还没有准备好处理这种事情;它需要更多的东西,比如(在调用之前不会检查概念)之类的东西。更简单的就是放弃一切,坚持使用单态 lambda。late_check
然而,随着C++0x概念的删除,多态lambda再次成为一个简单的命题。但是,我找不到任何建议。:(
评论
看看 Boost.Phoenix 的多态 lambdas: 顺便说一下,http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html 不需要 C++0x:)
评论
我想知道这个怎么办:
template <class something>
inline std::function<void()> templateLamda() {
return [](){ std::cout << something.memberfunc() };
}
我使用类似的代码来生成一个模板,并想知道编译器是否会优化“包装”函数。
评论
C++11 lambda 不能像其他答案中所述进行模板化,但在模板化类或函数中使用 lambda 时似乎会有所帮助。decltype()
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void boring_template_fn(T t){
auto identity = [](decltype(t) t){ return t;};
std::cout << identity(t) << std::endl;
}
int main(int argc, char *argv[]) {
std::string s("My string");
boring_template_fn(s);
boring_template_fn(1024);
boring_template_fn(true);
}
指纹:
My string
1024
1
我发现这种技术在使用模板化代码时很有帮助,但意识到它仍然意味着 lambda 本身不能被模板化。
评论
T
可以代替这个例子。decltype(t)
在 C++11 中,lambda 函数无法模板化,但在下一版本的 ISO C++ 标准(通常称为 C++14)中,将引入此功能。 [查看模板]
使用示例:
auto get_container_size = [] (auto container) { return container.size(); };
请注意,尽管语法使用关键字 ,但类型推导不会使用类型推演规则,而是使用模板参数推导规则。另请参阅通用 lambda 表达式的建议(以及对此的更新)。auto
auto
评论
auto
template
我知道这个问题是关于C++11的。但是,对于那些在谷歌上搜索并登陆此页面的人来说,C++ 14 现在支持模板化的 lambda,并且名称为 Generic Lambdas。
[信息]现在,大多数流行的编译器都支持此功能。Microsoft Visual Studio 2015 支持。Clang 支持。GCC 支持。
下面是一个解决方案,涉及将 lamba 包装在结构中:
template <typename T>
struct LamT
{
static void Go()
{
auto lam = []()
{
T var;
std::cout << "lam, type = " << typeid(var).name() << std::endl;
};
lam();
}
};
要使用,请执行:
LamT<int>::Go();
LamT<char>::Go();
#This prints
lam, type = i
lam, type = c
这样做的主要问题(除了额外的类型)你不能将这个结构定义嵌入到另一个方法中,否则你会得到(gcc 4.9)
error: a template declaration cannot appear at block scope
我也尝试过这样做:
template <typename T> using LamdaT = decltype(
[](void)
{
std::cout << "LambT type = " << typeid(T).name() << std::endl;
});
希望我能这样使用它:
LamdaT<int>();
LamdaT<char>();
但是我收到编译器错误:
error: lambda-expression in unevaluated context
所以这是行不通的......但即使它确实编译了,它也是有限的 use,因为我们仍然必须将“using LamdaT”放在文件范围内 (因为它是一个模板),这在某种程度上违背了 lambda 的目的。
我不确定为什么没有其他人提出这个建议,但您可以编写一个返回 lambda 函数的模板化函数。以下解决了我的问题,我来到此页面的原因:
template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
return [](DATUM datum){return 1.0;};
}
现在,每当我想要一个接受给定类型参数的函数(例如 ),我只需说std::string
auto f = makeUnweighted<std::string>()
现在返回 .f("any string")
1.0
这就是我所说的“模板化 lambda 函数”的一个例子。(这种特殊情况用于在某人不想对其数据进行加权时自动提供惰性加权函数,无论他们的数据可能是什么。
评论
有一个 gcc 扩展允许 lambda 模板:
// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
(boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
pair.second = new Widget_T();
pair.second->set_label_str(Key_T::label);
}
);
其中 is a_widgets
std::tuple< fusion::pair<Key_T, Widget_T>... >
评论
我一直在使用最新的 clang 编译标志,现在对 lambda 的自动类型参数有一些很好的支持:version 5.0.1
-std=c++17
#include <iostream>
#include <vector>
#include <stdexcept>
int main() {
auto slice = [](auto input, int beg, int end) {
using T = decltype(input);
const auto size = input.size();
if (beg > size || end > size || beg < 0 || end < 0) {
throw std::out_of_range("beg/end must be between [0, input.size())");
}
if (beg > end) {
throw std::invalid_argument("beg must be less than end");
}
return T(input.begin() + beg, input.begin() + end);
};
auto v = std::vector<int> { 1,2,3,4,5 };
for (auto e : slice(v, 1, 4)) {
std::cout << e << " ";
}
std::cout << std::endl;
}
在 C++20 中,可以使用以下语法实现此操作:
auto lambda = []<typename T>(T t){
// do something
};
评论
[]<>(){}
T t
lambda.template operator()<int>()
lambda<int>()
lambda
<
lambda
operator()
lambda<a>(b)
lambda.operator()<a>(b)
lambda.operator<(a).operator>(b)
C++11 的另一个解决方法是定义模板函数并将其包装在 lambda 表达式中。然而;这需要为不同的模板化 lambda 定义一个新函数:
struct ST{ int x; };
template<class T>
T templateFunc(T variable)
{
return variable;
}
void func()
{
ST st{10};
auto lambda = [&](){return templateFunc<ST>(st);};
auto res = lambda();
}
正如其他人所说,您可以在 C++20 lambda 中使用模板参数。
auto doSomething = []<typename T>(T x) {
// blabla
};
但是,其他答案忘记告诉您如何调用这些 lambda。
在前面的示例中,这很容易,因为所有模板类型都可以从提供的参数中推导出来。
doSomething();
但是,当无法从参数中推断出模板类型时,问题就来了。
auto readValue = [&buffer]<typename T>() -> T{
return *(const T*)buffer;
};
您可能希望像常规模板化函数一样调用它:
readValue<int>(); // WRONG!
但这不会编译。你必须这样写:
readValue.template operator()<int>(); // VALID!
这有点冗长,但您可以将 lambda “展开”到可调用对象中,并在类定义上使用模板。您甚至可以捕获变量,像使用 lambda 一样将对象存储到变量中,或者为单个调用实例化新对象。 可能性是无穷无尽的。
template <typename T>
struct Callable
{
Callable(int& i) : capturedInt(i) {}
T operator()(T a, T b)
{
T total = a;
for (int i = 0; i < capturedInt; i++)
total *= b;
return total;
}
private:
int& capturedInt;
};
int main() {
int i = 5;
Callable<float> callable(i);
cout << "Result of callable(5)(10, 1.5f): " << callable(10, 1.5f) << '\n';
i = 6;
cout << "Result of callable(6)(1.5f, 10): " << callable(1.5f, 10) << '\n';
i = 10;
cout << "Result of Callable<int>(10)(2, 3): " << Callable<int>(i)(2, 3) << '\n';
return 0;
}
输出:
Result of callable(5)(10, 1.5f): 75.9375
Result of callable(6)(1.5f, 10): 1.5e+06
Result of Callable<int>(10)(2, 3): 118098
评论