可以对lambda函数进行模板化吗?


230

在C ++ 11中,有没有办法为lambda函数建立模板?还是固有地太具体而无法模板化?

我了解我可以改为定义经典的模板化类/函数,但问题更像是:该语言是否允许模板化lambda函数?


有没有用过lambda模板有用的用例?
James McNellis 2010年

7
詹姆斯:您可以构建一个函数来遍历元组(不一定有用)。
乔D

在阅读Stroustrup的采访时,我想到了这个主意,当时他谈到元模板的复杂性是一个问题。如果允许的话,我想象的是忍者代码赋可能是太聪明的程序员使用这种功能组合而发明的……
Klaim 2010年

Answers:


181

更新2018:C ++ 20将带有模板化和概念化的lambda。该功能已集成到标准草稿中。


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


就目前而言(在C ++ 11中),可惜没有。就灵活性和功能而言,多态lambda极佳。

他们最终变得单态的最初原因是由于概念。概念使此代码情况变得困难:

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

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

概念还没有准备好解决这类问题。它需要更多的东西late_check(例如,直到调用该概念才对其进行检查)和其他东西。更简单的是放弃所有内容并坚持使用单态lambda。

但是,随着C ++ 0x中概念的删除,多态lambda再次成为一个简单的命题。但是,我找不到任何建议。:(


5
简单...除了希望重新引入概念并避免使它们变得复杂的功能。

6
我想我宁愿拥有多态lambda而不是概念。我不明白这个例子是如何激发一切的。您可以简单地将其禁止为错误,并要求lambda为单态[](T x){}或受约束的模板[] template <Constraint T>(T x){},这些模板可以进行静态验证以匹配。是否有某些原因导致无法执行此操作?
DrPizza

13
您不必在概念和多态lambda之间进行选择:cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams

3
以下是有关多态lambda的建议:open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf和clang中的玩具实现:faisalv.github.com/clang-glambda
Radif Sharafullin

18
多态Lambdas将使用C ++ 14,至少现在它们在社区草案中:)
Arne Mertz,

37

C ++ 11 lambda无法像其他答案中所述进行模板化,但是decltype()在模板化类或函数中使用lambda时似乎有所帮助。

#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本身进行模板化。


26
T可以代替decltype(t)此示例。
user2023370

26

在C ++ 11中,无法对lambda函数进行模板化,但是在下一个版本的ISO C ++ Standard(通常称为C ++ 14)中,将引入此功能。[资源]

用法示例:

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

请注意,尽管语法使用关键字auto,但类型推导将不使用auto类型推导的规则,而是使用模板参数推导的规则。另请参见有关通用lambda表达式建议(以及对此的更新)。


5
auto类型推导的规则被专门定义为与template函数自变量推导的规则相同。
underscore_d

10

我知道这个问题与C ++ 11有关。但是,对于那些谷歌搜索和登陆该页面的人,C ++ 14现在支持模板化的lambda,并以Generic Lambdas命名。

[info]大多数流行的编译器现在都支持此功能。Microsoft Visual Studio 2015支持。lang支持。GCC支持。


6

我不知道这是怎么回事:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

我使用了类似的代码来生成模板,并想知道编译器是否会优化“包装”功能。


2
什么编译器?做到了?
NicoBerrogorry


3

有一个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);
                          }
              );

哪里_widgetsstd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW,这已成为C ++ 20中的标准语法。
LF

2

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

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

1

这是涉及将兰巴包在结构中的一种解决方案:

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

因此,这是行不通的……但是,即使它进行了编译,它的用途也很有限,因为我们仍然必须将“ using LamdaT”放在文件作用域中(因为它是模板),这有点违背了lambdas。


1

我不确定为什么没有其他人建议这样做,但是您可以编写一个返回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函数”的例子。(当某人不想对他们的数据进行加权时(不管他们的数据可能是什么),此特殊情况用于自动提供惰性加权功能。)


2
仅当在创建lambda之前知道lambda的参数类型时,此方法才有效,在这种情况下,您可以仅使用具有特定类型的lambda作为参数。多态lambda的要点是为您在编写工作代码时不知道的参数类型提供要完成的工作。基本上,这是完全不同的,这就是为什么不建议这样做的原因。
Klaim '16

啊,对,明白了。我没有想到这种用例-我认为lambda函数是即时的东西,而这种多态性则是多功能库中的东西。我正在编写一个模板库,该库需要接受任何类型的用户的lambda函数,还需要提供正确类型的默认值。
Jim Pivarski '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.