【问题标题】:Can lambda functions be templated?可以对 lambda 函数进行模板化吗?
【发布时间】:2011-04-04 07:18:23
【问题描述】:

在 C++11 中,有没有办法对 lambda 函数进行模板化?还是它本身就太具体而无法模板化?

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

【问题讨论】:

  • 是否存在 lambda 模板有用的用例?
  • James:你可以构建一个函数来迭代一个元组(不一定有用)。
  • 我在阅读 Stroustrup 的采访时想到了这个想法,他谈到元模板的复杂性是一个问题。如果允许的话,我在想象 ninja code-fu 可能是由过于聪明的程序员在玩这种功能组合时发明的......
  • 十年前的历史观点很有趣。
  • 确实如此。现在我看到了它是如何有用的。我也可以看到它是如何被滥用的,但所有有用的工具都可以。 :做鬼脸:

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


【解决方案1】:

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

【讨论】:

  • 简单...除了希望重新引入概念并避免使它们变得复杂的功能。
  • 我想我宁愿拥有多态 lambdas 而不是概念。我不明白这个例子是如何激发任何东西的。您可以简单地禁止它作为错误,并要求 lambda 是单态的 [](T x) {} 或受约束的模板 []template(T x) {},可以静态验证以匹配。有什么原因导致这不可能吗?
  • 您不必在概念和多态 lambda 之间做出选择:cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
  • 这里是多态 lambda 的提议:open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf 和 clang 中的玩具实现:faisalv.github.com/clang-glambda
  • 多态 Lambda 将在 C++14 中,至少它们现在在社区草案中 :)
【解决方案2】:

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 本身不能被模板化。

【讨论】:

  • T 在此示例中可以很好地代替 decltype(t)
【解决方案3】:

在 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)。

【讨论】:

  • auto类型推导规则具体定义与template函数实参推导规则相同。
【解决方案4】:

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

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

【讨论】:

  • 这是否意味着我们现在可以编写这样的 lambda []&lt;&gt;(){}
  • 如果您需要在调用 lambda 时显式指定模板参数,我发现这在我的用例中很常见(例如,如果上面的示例中不存在 T t 参数) ,那么您可以使用以下语法:lambda.template operator()&lt;int&gt;()。请参阅stackoverflow.com/questions/49392738 了解更多信息。
【解决方案5】:

我知道这个问题是关于 C++11 的。但是,对于那些在谷歌上搜索并登陆此页面的人,C++14 现在支持模板化 lambda,并以通用 Lambda 的名称命名。

[info] 现在大多数流行的编译器都支持这个特性。 Microsoft Visual Studio 2015 支持。 Clang 支持。 GCC 支持。

【讨论】:

    【解决方案6】:

    我想知道这是怎么回事:

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

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

    【讨论】:

    • 什么编译器?有吗?
    【解决方案7】:

    有一个 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&lt; fusion::pair&lt;Key_T, Widget_T&gt;... &gt;

    【讨论】:

    • FWIW,这已成为 C++20 的标准语法。
    【解决方案8】:

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

    【讨论】:

    • 我已经知道了,但问题完全是关于新标准的;)
    • 好的 :) C++0x lambdas 是单态的,不能被模板化,很遗憾。
    【解决方案9】:

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

    【讨论】:

    • 只有在创建 lambda 之前知道 lambda 的参数类型时才有效,在这种情况下,您可以只使用具有特定类型的 lambda 作为参数。多态 lambda 的重点是提供要在编写工作代码时对您永远不知道的参数类型完成的工作。基本上,这是完全不同的,这就是为什么不建议这样做。
    • 啊,对了,明白了。我没有想到那个用例——我认为 lambda 函数是即时的东西,而那种多态性是多用途库中的东西。我正在编写一个模板库,它需要接受任何类型的用户 lambda 函数,并提供正确类型的默认值。
    【解决方案10】:

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

    【讨论】:

      【解决方案11】:

      这是一种解决方案,涉及将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
      

      所以这不起作用......但即使它确实编译了它也将是有限的 使用,因为我们仍然必须将“使用 LamdaT”放在文件范围内 (因为它是一个模板)这有点违背了 lambdas 的目的。

      【讨论】:

        【解决方案12】:

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

        【讨论】:

          猜你喜欢
          • 2018-04-25
          • 2015-11-19
          • 2022-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多