【问题标题】:C++11 and the lack of polymorphic lambdas - why?C++11 和缺少多态 lambda - 为什么?
【发布时间】:2011-06-06 07:05:07
【问题描述】:

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

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

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

原因是什么:

  • 是委员会没时间了吗?

  • 多态 lambda 太难实现了?

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

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

相关来源:

【问题讨论】:

  • 该死的,多么混乱的语法。
  • 语法有什么问题?它实际上相当不错。
  • @Dominar 这就是“关闭”的意思。 en.wikipedia.org/wiki/Closure_(computer_programming)
  • @Kirakun:删除所有被后来的扩展变得多余的东西(例如删除除统一初始化语法之外的所有形式的初始化)将是一个有趣的实验,保留 abstract i> 用于 C++ 非冗余子集的语法与现在相同,但设计一个新的 具体 语法更符合 Scala 和/或 Cobra 和/或 Ruby(取决于您是否更喜欢大括号、缩进或关键字)。我敢打赌,你会得到一些与 C++ 100% 同构的相当漂亮的语言。
  • 嗯。我可以没有它。 [](decltype(*begin) t) { ++t; }

标签: c++ lambda c++11 standards polymorphism


【解决方案1】:

this posting 很好地解释了我们没有多态 lambda 的原因。

这与从 C++11 中提取的概念特性有关:本质上,多态 lambda 是普通的、不受约束的函数模板,我们不知道如何对使用不受约束的模板的概念约束模板进行类型检查。但是,解决这个问题很容易,如here(死链接)所示,所以我认为没有任何障碍。

cpp-next 的链接已失效;相关信息可以找到here

【讨论】:

  • 请注意,我们的多态 lambdas 的 proposal 得到了波特兰 Evolution 工作组的好评,所以如果我们根据这些 cmets 改进提案,我想我们会在 C 中看到这个特性++2014.
  • 第二个链接失效了。
【解决方案2】:

由于参数 c 满足容器的 STL 要求,您应该可以使用类似的东西

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

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

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

(是的,Dominar,我知道你不喜欢这个答案,因为它没有回答你的问题,但我敢打赌,下一个提出这个问题的人会寻找一种使他们的代码工作的方法,因此在与问题相关的地方使用一些技术确实很有意义。)

【讨论】:

  • Ken:不知道你在做什么,因为编译器已经做了非常相似的事情:codepad.org/BoaD4Mhi
  • 谁支持这个答案?这不是正确答案,不会被选为正确答案。
  • @Dominar:你是对的。我认为我的思想已经被 Scala 泛型或其他东西破坏了。我无法完全弄清楚 C++ 编译器如何做到这一点的心理体操。
  • @Dominar:我已经删除了答案中错误的部分。其他人将不得不解释设计背后的理论。 (我认为他们对我投了赞成票,因为答案是让你的代码工作的实用方法。)
  • @Ken:事实上可以,这种类型推断的基础工作在 03 标准中被设置。请删除您的答案,因为我只是在寻找正确的答案,我不想派人去追逐野鹅红鲱鱼:D
【解决方案3】:

这可能是因为已经有一种语法可以做到这一点,而 lambdas 的目的是引入一种更简单的语法来涵盖大多数情况。当您尝试涵盖所有情况时(如果您希望自动生成的仿函数继承特定的基类怎么办?),您将失去 lambda 的相对优势(简单性和简洁性)。

我真的不喜欢建议的语法。 T 是关键字吗?名称查找失败的所有标识符是否都会自动转换为模板类型名参数?这可以防止您检测到拼写错误,而 IMO 是一个BAD 的想法:

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

它还引入了远距离动作行为,如果在某个头文件中的某个地方引入了命名类型,则含义会突然改变。也真的很糟糕

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

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

这是明确的,现在允许非类型模板参数(用于通过引用接受数组),但真的很笨拙。此时你最好手动写出函子,它会更容易理解。

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

for_each(c.begin(),c.end(),[](auto& t) { ++t; });

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

但是现在你有一个问题,for_each 推断一个类型名模板参数,而不是一个模板模板参数。在这种情况下,类型推断是不可能的。

在当前提案中,lambdas 具有类型,即使它是不可提及的(decltype 除外)类型。您必须失去该功能才能在调用站点进行推理。

显示问题不是 lambda 的缺点的示例,它只是一个不可演绎的上下文:

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

必须明确指定std::min 的模板类型参数。在这方面,Lambda 与使用现有函子没有什么不同。

编辑:好的,现在我意识到我们并不是建议 lambda 生成一个模板仿函数类型,而是一个实现模板函数应用运算符 (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; }

好的,这已经足够好了,但是如果我们想要两个参数但只有一个类型参数怎么办:

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

看起来不错,但我发现语法具有误导性。语法表明类型参数是从第一个实参推断出来的,第二个参数被强制转换为相同的类型,但实际上在类型推断过程中两个实参被认为是相等的。

也许最好将这种情况限制为每个类型参数一个 lambda 参数,如果您想要更多限制,请自己编写仿函数。在我看来,这似乎是灵活性和功能与保持语法简单之间的良好折衷。

【讨论】:

  • 有趣的结论,但我认为它们通常无效,请阅读以下内容:open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1375.html 正如我在问题底部提到的,示例只是一个示例。但 +1 是一个比现有答案更好的答案。
  • 所以澄清一下,(auto&amp; t) 语法实际上并不能工作,但你认为 C++ 标准委员会应该让它工作,因为它捕获了这个非常合理的用例,而没有 lambda 语法太糟糕了。
  • 如果他们在标准库中有类似:template &lt;typename T&gt; using id = T; 的内容,那么您可以通过[](auto&amp; x, std::id&lt;x&gt;&amp; y) 停止推理。我认为它仍然可行,只是需要更多实用功能。 Roger Pate 和我在他离开前不久讨论过这个问题。使用新语言,您实际上可以摆脱显式模板语法,而只需在参数类型中使用auto。 (任何这样做的函数都有一个隐含的template &lt;typename __...&gt;。)它将大大简化模板。
  • @GMan:那实际上不需要像std::id&lt;decltype(x)&gt; 这样的东西吗?变得丑陋,但也许是必要的。而且我不认为auto 在一般情况下可以替换显式模板表示法,但它肯定会是一个很好的简写,可以简化大部分模板函数的编写。
  • 为什么不添加更多括号呢? &lt;typename T&gt;[](T&amp; x, T&amp; y){x++; y--;}
【解决方案4】:

好吧,既然您已经链接了n1968,那么您的问题的答案就很明显了。可在提案的第 5.1 节中找到。

【讨论】:

  • 是的。但是,天哪,我不能说我同意这个推理。我开始怀疑添加概念是否完全可取。它应该改进模板错误消息,而不是阻止直观和有用的语言功能的实现。
  • @jalf:我很确定这就是他们死的原因。概念非常复杂,就像在第一种语言的基础上学习第二种语言一样。
  • @GMan:但是......我认为官方的说法是他们实际上并没有死,他们只是错过了最后期限。虽然最初提出的语法很可能已经死了。
  • @Ben:对不起,我的措辞模棱两可。 “死”的意思是“没有使其[进入新标准]”。
  • 是的,但我开始怀疑他们是否已经死了够了。我对概念提案了解得越多,我就越觉得它被误导了。当然,太大太野心勃勃,但也损害了 C++ 语言的许多有价值的方面,可能使通用代码更难编写。所以如果Concepts复活,我希望他们能后退一大步,真正重新考虑他们想要实现的目标。 @Ben,我认为他们已经说过他们的目标是未来 5 年的时间表,因此您可能会在不到十年的时间内获得新功能。 ;)
【解决方案5】:

following(您对我上面其他答案的评论)有效:

#include <algorithm>
#include <vector>

struct foo
{
   template<typename T>
   void operator()(T& t)
   {
      ++t;
   }
};

int main()
{

   std::vector<int> v;
   std::for_each(v.begin (),v.end(),foo());

   return 0;
}

但以下不是:

#include <algorithm>
#include <vector>

template<typename T>
struct foo
{
   void operator()(T& t)
   {
      ++t;
   }
};

int main()
{

   std::vector<int> v;
   std::for_each(v.begin (),v.end(),foo()); // <-- the syntax for foo here 
                                            //     is kinda fictitious

   return 0;
}

C++ 委员会可能认为 lambdas 与第二个示例比第一个示例更相似。 (虽然我还没有想出聪明的方法来定义一个 lambda,这会有所作为。有人有什么疯狂的想法吗?)

【讨论】:

  • @Ken:因为 foo() 是一个内联实例化,你需要在实例化时专门化它 - 没有什么疯狂的,如果你做了 foo() 它会起作用。 codepad.org/VtLmqNlW
  • @Ben:我删除了它,因为它与 Ken 的代码不完全一样,我认为与 Ken 的代码完全一样的东西会更好,因为他似乎只理解非常狭窄/严格的定义的问题。
  • @Ken:这对于理解并知道问题所在的人来说很好,你已经证明你没有掌握任何一个。所以你不编辑是有道理的,至于我为什么发表原始评论,通过使用 std::iterator_traits 你可以通过 value_type 推断类型,所以你的改变真的没有增加任何东西,实际上增加了更多的混乱。从一开始你就可以做的最好的事情就是删除你的答案,继续关注问题,让其他人教育你。
  • @MSalters:正在讨论的编辑是关于问题而不是答案。而 lambda 绝对是实现 operator()() 的对象的语法糖。
  • @MSalters:我认为你应该学会阅读这个问题,只是出于好奇,你不会碰巧成为对 Ken Blooms 答案投票的傀儡之一吗?对于一个有 27k 点的人,你似乎对这个主题没有了解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 1970-01-01
  • 2021-12-24
相关资源
最近更新 更多