lambda函数可以被模板化吗?

在c++ 11中,是否有一种方法来模板lambda函数?还是它本身就太具体了,无法被模板化?

我知道我可以定义一个经典的模板化类/函子,但问题更像是:语言是否允许模板化lambda函数?

148530 次浏览

更新2018:c++ 20将带来模板化和概念化的lambdas。该特性已经集成到标准草案中。


更新2014:c++ 14已于今年发布,现在提供了与本例相同语法的多态lambdas。一些主要的编译器已经实现了它。


在c++ 11中,遗憾的是没有。多态lambdas在灵活性和功能方面非常出色。

它们最终变成单形的最初原因是因为概念。概念使这种代码情况变得困难:

template <Constraint T>
void foo(T x)
{
auto bar = [](auto x){}; // imaginary syntax
}

在一个受约束的模板中,只能调用其他受约束的模板。(否则约束将无法检查。)foo可以调用bar(x)吗?lambda有什么约束(毕竟它的参数只是一个模板)?

概念还没有准备好解决这类问题;它需要更多的东西,如late_check(概念在调用之前不检查)和其他东西。更简单的方法是放弃所有这些,坚持用单形。

然而,随着c++ 0x中概念的移除,多态lambda又变成了一个简单的命题。但是,我找不到任何建议。:(

看看Boost。Phoenix for polymorphism 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

我发现这种技术在处理模板化代码时很有帮助,但意识到它仍然意味着lambdas本身不能被模板化。

在c++ 11中,lambda函数不能被模板化,但在ISO c++标准的下一个版本(通常称为c++ 14)中,将引入此特性

使用的例子:

auto get_container_size = [] (auto container) { return container.size(); };

注意,虽然语法使用了关键字auto,但类型推导不会使用auto类型推导的规则,而是使用模板参数推导的规则。另请参阅泛型lambda表达式的建议(以及this的更新)。

我知道这个问题是关于c++ 11的。然而,对于那些在谷歌上搜索并登陆这个页面的人来说,c++ 14现在支持模板化lambdas,并被称为泛型lambdas。

现在大多数流行的编译器都支持这个特性。微软Visual Studio 2015支持。叮当声的支持。GCC支持。

这里有一个解决方案,涉及到将lamba包裹在一个结构中:

template <typename T>
struct LamT
{
static void Go()
{
auto lam = []()
{
T var;
std::cout << "lam, type = " << typeid(var).name() << std::endl;
};


lam();
}
};

使用do:

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

所以这行不通…但是即使它编译了,它的作用也是有限的 使用,因为我们仍然必须把“使用LamdaT”在文件范围 (因为它是一个模板)这在某种程度上违背了lambdas的目的。< / p >

我不知道为什么没有人建议这样做,但是您可以编写一个返回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

这就是我所说的“模板化函数”的一个例子。(这个特殊的情况是用来自动提供一个惰性加权函数,当有人不想加权他们的数据时,不管他们的数据是什么。)

有一个gcc扩展,它允许λ模板:

// 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);
}
);

其中_widgetsstd::tuple< fusion::pair<Key_T, Widget_T>... >

我一直在使用最新的clang version 5.0.1编译,使用-std=c++17标志,现在对lambdas的自动类型参数提供了一些很好的支持:

#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
};

c++ 11的另一个解决方法是定义一个模板函数并将其包装在lambda表达式中。然而;这需要为不同的模板化lambdas定义一个新函数:

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();
}