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

发布于 2024-10-14 01:03:55 字数 1008 浏览 3 评论 0原文

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

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

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

原因是什么:

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

相关来源:

I've been reviewing the draft version of the C++11 standard. Specifically the section on lambdas, and I am confused as to the reasoning for not introducing polymorphic lambdas.

For example, amongst the 100001 ways polymorphic lambdas could be used, I had hoped we could use code such as the following:

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

What were the reasons:

  • Was it that the committee ran out of time?

  • That polymorphic lambdas are too hard to implement?

  • Or perhaps that they are seen as not being needed by the PTB?

Note: Please remember the example above is not the only one, and it is only provided as a guide to the types of code. Answers that solely concentrate on providing a workaround for the above piece of code will not be considered as valid!

Related sources:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

入怼 2024-10-21 01:03:55

这篇文章很好地解释了我们没有多态 lambda 的原因。

它与从 C++11 中提取的概念功能有关:本质上,多态 lambda 是普通的、无约束的函数模板,我们不知道如何对使用无约束模板的概念约束模板进行类型检查。然而,解决这个问题很容易,如此处< /a>(死链接),所以我认为没有任何障碍。

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

The reason we don't have polymorphic lambdas is explained pretty well in this posting.

It has to do with the concepts feature that was pulled from C++11: essentially, polymorphic lambdas are ordinary, unconstrained function templates and we didn't know how to typecheck a concept-constrained template that used an unconstrained template. However, solving that problem turns out to be easy as shown here(dead link), so I don't think there's any obstacle remaining.

The link to cpp-next is dead; the relevant info can be found here

金橙橙 2024-10-21 01:03:55

由于参数 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,我知道你不喜欢这个答案,因为它不不回答你的问题,但我愿意打赌,下一个提出这个问题的人将会寻找一种方法来让他们的代码工作,所以围绕问题所在使用一些技术确实是有意义的是相关的。)

Since the argument, c, meets the STL requirements for a container, you should be able to use something like

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

I'll also showcase John Purdy's comment above, which is another way to get the typename you want in this lambda:

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

(Yes, Dominar, I know you don't like this answer, because it doesn't answer your question, but I'm willing to bet that the next person who comes along asking this question is going to be looking for a way to make their code work, so it does make sense to have some techniques around where the question is relevant.)

杀手六號 2024-10-21 01:03:55

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

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

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()()< /code>

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

在当前提案中,lambda 具有类型,即使它是不可提及的类型(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 也不太满意。带有多个参数的 lambda 怎么样:

[](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 参数,如果您想要更多限制,请自己编写函子。在我看来,这是灵活性和功能与保持语法简单之间的良好折衷。

It's probably because there already is a syntax for doing that, and the purpose of lambdas is to introduce a much simpler syntax that covers most cases. When you try to cover all cases (what if you wanted the auto-generated functor to inherit a particular base class?), you lose the comparative advantages (simplicity and terseness) of the lambda.

I really don't like the proposed syntax. Is T a keyword? Do all identifiers for which name lookup fails get turned automatically into template typename arguments? That prevents you from detecting misspellings, which IMO is a BAD idea:

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

It also introduces action-at-a-distance behavior, if the named type get introduced in some header file somewhere, the meaning changes suddenly. Also really BAD.

Well, since it's supposed to create a template, we could borrow the existing syntax:

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

This is unambiguous and now allows non-type template arguments (useful for accepting arrays by reference), but is really unwieldy. At this point you're better off writing out the functor by hand, it'll be much easier to understand.

However, I think a simple syntax is possible using the auto keyword:

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

This next section incorrectly assumes that the template parameter appears on the functor type rather than its operator()():

But now you have a problem that for_each infers a typename template argument, not a template template argument. Type inference isn't possible in that context.

In the current proposal, lambdas have type, even if it's an unmentionable (other than decltype) type. You'd have to lose that feature in order to accommodate inference at the call-site.

Example showing that the issue is NOT a shortcoming of lambdas, it's simply a non-deducible context:

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

The template type parameter to std::min must be explicitly specified. Lambdas are no different from using existing functors in this regard.

EDIT: Ok, now that I realize we aren't suggesting that the lambda generate a template functor type, but a single non-template functor type which implements a templated function application operator (operator()()), I agree that the compiler should be able to generate such a thing. I propose that using the auto keyword here would be a good simple syntax for requesting that.

However, I'm not really happy with auto either. What about lambdas with multiple parameters:

[](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; }

Ok, that works well enough, but what if we wanted two parameters but only one type argument:

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

Seems ok, but I find the syntax misleading. The syntax suggests that the type parameter is inferred from the first actual parameter, and the second parameter is coerced to the same type, but actually both actual parameters are considered equal during type inference.

Perhaps it's best that this case be limited to one lambda parameter per type argument, and if you want something more constrained, write the functor yourself. This seems to me to be a good compromise between flexibility and power vs keeping the syntax simple.

心在旅行 2024-10-21 01:03:55

Well, now that you've linked n1968, the answer to your question is apparent. It's found in section 5.1 of the proposal.

Spring初心 2024-10-21 01:03:55

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

#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 的巧妙方法,这会产生影响。有人有任何疯狂的想法吗?)

The following (your comment to my other answer above) works:

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

But the following does not:

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

Probably the C++ committee saw lambdas as being more similar to the second example than the first. (Though I haven't figured out clever way to define a lambda in which this would make a difference. Anyone got any crazy ideas?)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文