C++ 11和缺乏多态lambda - 为什么?

36 c++ polymorphism lambda standards c++11

我一直在审查C++ 11标准的草案版本.特别是关于lambdas的部分,我对于不引入多态lambda的原因感到困惑.

例如,在100001种方式中可以使用多态lambda,我曾希望我们可以使用如下代码:

template<typename Container>
void foo(Container c)
{
    for_each(c.begin(), c.end(), [](T& t) { ++t; });
}
Run Code Online (Sandbox Code Playgroud)

原因是什么:

  • 是不是委员会没时间了?

  • 多态lambda太难实现了吗?

  • 或者也许他们认为PTB不需要它们?

注意:请记住上面的示例不是唯一的例子,它仅作为代码类型的指南提供.仅专注于为上述代码提供解决方法的答案将不被视为有效!

相关来源:

Dav*_*ams 51

我们没有多态lambda的原因在这篇文章中得到了很好的解释.

它与从C++ 11中提取的概念特性有关:基本上,多态lambda是普通的,无约束的函数模板,我们不知道如何使用无约束模板对概念约束模板进行类型检查.然而,解决了问题,原来如图所示很容易在这里(死链接),所以我不认为有任何剩余的障碍.

到cpp-next的链接已经死了; 相关信息可以在这里找到

  • 请注意,波特兰的进化工作组对我们的[提案](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf)的多态性lambdas进行了有利的观察,因此,如果我们根据这些评论改进提案,我想我们会在C++ 2014中看到这个功能. (2认同)

Ken*_*oom 16

由于参数c符合容器的STL要求,因此您应该可以使用类似的东西

template<typename Container>
void foo(Container c)
{
    for_each(c.begin(), c.end(),[](typename Container::reference t) { ++t; });
}
Run Code Online (Sandbox Code Playgroud)

我还将在上面展示John Purdy的评论,这是获取此lambda中所需类型名称的另一种方法:

template<typename Container>
void foo(Container c)
{
   for_each(c.begin(),c.end(),[](decltype(*c.begin()) t) { ++t; });
}
Run Code Online (Sandbox Code Playgroud)

(是的,Dominar,我知道你不喜欢这个答案,因为它没有回答你的问题,但是我愿意打赌下一个提出这个问题的人会找一个方法来让他们的代码工作,所以在问题相关的地方有一些技巧是有意义的.)

  • 谁回答了这个答案?这不是正确答案,不会被选为正确答案. (13认同)
  • 肯:不知道你在做什么,因为编译器已经做了类似的事情:http://codepad.org/BoaD4Mhi (7认同)
  • @Ken:实际上它可以,这种类型演绎的基础工作在03标准中被设定.请删除你的答案,因为我只是在寻找正确的答案,我讨厌派人去做一对野鹅鲱鱼追逐:D (5认同)

Ben*_*igt 7

这可能是因为已经有了这样做的语法,lambdas的目的是引入一个更简单的语法来覆盖大多数情况.当你试图涵盖所有情况时(如果你想让自动生成的函子继承特定的基类怎么办?),你将失去lambda的比较优势(简单性和简洁性).

我真的不喜欢提出的语法.是T关键字吗?所有名称查找失败的标识符是否会自动转换为模板typename参数?这可以防止你发现拼写错误,IMO是一个不好的想法:

for_each(c.begin(),c.end(),[](iterater& t) { ++t; });
// programmer misspelled "iterator" and now has a polymorphic lambda, oops
Run Code Online (Sandbox Code Playgroud)

它还引入了远程动作行为,如果命名类型在某个头文件中引入,则意义突然改变.也真的很糟糕.

好吧,因为它应该创建一个模板,我们可以借用现有的语法:

for_each(c.begin(),c.end(),[]template<typename T>(T& t) { ++t; });
Run Code Online (Sandbox Code Playgroud)

这是明确的,现在允许非类型模板参数(对于通过引用接受数组很有用),但实际上并不笨重.在这一点上,你最好用手写出仿函数,它会更容易理解.

但是,我认为使用auto关键字可以使用简单的语法:

for_each(c.begin(),c.end(),[](auto& t) { ++t; });
Run Code Online (Sandbox Code Playgroud)

下一节错误地假设模板参数出现在仿函数类型而不是它operator()():

但现在您遇到的问题是for_each推断出typename模板参数,而不是模板模板参数.在该上下文中不可能进行类型推断.

在当前的提议中,lambdas具有类型,即使它是一个不可知的(除了decltype)类型.您必须丢失该功能才能适应呼叫站点的推断.

示例显示问题不是lambdas的缺点,它只是一个不可导入的上下文:

#include <vector>
#include <algorithm>
#include <iterator>

int main(void)
{
    using namespace std;
    vector<int> a(10);
    vector<int> b(10);
    vector<int> results;

    transform(a.begin(), a.end(), b.begin(), back_inserter(results), min<int>);
}
Run Code Online (Sandbox Code Playgroud)

std::min必须显式指定模板类型参数.在这方面,Lambdas与使用现有的仿函数没有什么不同.

编辑:好的,现在我意识到我们并没有建议lambda生成模板函子类型,而是实现模板化函数应用程序operator(operator()())的单个非模板函子类型,我同意编译器应该能够生成这样的事情.我建议在auto这里使用关键字是一个很好的简单语法来请求.

但是,我对这auto两者都不满意.那些有多个参数的lambdas怎么样:

[](auto& x, auto& y){ return x + y; }
//becomes
template<typename T1, typename T2>
auto operator()(T1& x, T2& y) -> decltype(x + y) { return x + y; }
Run Code Online (Sandbox Code Playgroud)

好的,这很好用,但是如果我们想要两个参数但只有一个类型参数呢:

[](auto& x, decltype(x)& y){ return x + y; }
//becomes
template<typename T1>
auto operator()(T1& x, T1& y) -> decltype(x + y) { return x + y; }
Run Code Online (Sandbox Code Playgroud)

似乎没问题,但我觉得语法有误导性.语法建议从第一个实际参数推断出类型参数,并且第二个参数被强制转换为相同类型,但实际上两个实际参数在类型推断期间被认为是相等的.

也许最好将这种情况局限于每个类型参数的一个lambda参数,如果你想要更多约束,可以自己编写仿函数.在我看来,这是灵活性和功能之间的良好折衷,而不是保持语法简单.

  • 如果他们在标准库中有一些东西,比如:`template <typename T> using id = T;`那么你可以做`[](auto&x,std :: id <x>&y)`来停止扣除.我认为它仍然可行,只需要更多实用功能.罗杰佩特和我在他离开前不久前讨论过这个问题.使用新语言,您实际上可以摆脱显式模板语法,只需在参数类型中使用`auto`.(任何这样做的函数都有一个隐含的`template <typename __...>`.)它会大大简化模板. (3认同)