lambda函数可以模板化吗?

时间:2010-08-26 14:07:02

标签: c++ templates lambda c++11

在C ++ 11中,有没有办法模板化lambda函数?或者它本身是否太具体而无法模板化?

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

12 个答案:

答案 0 :(得分:155)

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


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


现在(在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再次成为一个简单的命题。但是,我找不到任何建议。 :(

答案 1 :(得分:34)

C ++ 11 lambdas不能像其他答案中所述那样进行模板化,但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

我发现这种技术在使用模板化代码时很有帮助,但实现它仍然意味着lambdas本身无法模板化。

答案 2 :(得分:22)

在C ++ 11中,lambda函数不能模板化,但在下一版本的ISO C ++标准(通常称为C ++ 14)中,将引入此功能。 [Source]

用法示例:

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

请注意,虽然语法使用关键字auto,但类型推导不会使用auto类型推导的规则,而是使用模板参数推导的规则。另请参阅proposal for generic lambda expressions(以及此update)。

答案 3 :(得分:9)

我知道这个问题是关于C ++ 11的。但是,对于那些使用Google搜索并登陆此页面的人,现在在C ++ 14中支持模板化lambda,并使用名称Generic Lambdas。

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

答案 4 :(得分:6)

我想知道这是怎么回事:

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

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

答案 5 :(得分:6)

在C ++ 20中,可以使用以下语法:

auto lambda = []<typename T>(T t){
    // do something
};

答案 6 :(得分:4)

看看Boost.Phoenix的多态lambda:http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html 顺便说一下,不要求C ++ 0x:)

答案 7 :(得分: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>... >

答案 8 :(得分: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函数”的一个例子。 (这种特殊情况用于在某人不想对数据进行加权时自动提供惰性加权函数,无论数据是什么。)

答案 9 :(得分:1)

我一直在使用最新的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;
}

答案 10 :(得分:0)

这是一个涉及将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的目的。

答案 11 :(得分:0)

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