我一直在审核C++11标准的草案版本。特别是关于lambdas的部分,我对于不引入多态lambda的原因感到困惑。
例如,在100001种方式中可以使用多态lambda,我希望我们可以使用如下代码:
template<typename Container>
void foo(Container c)
{
for_each(c.begin(), c.end(), [](T& t) { ++t; });
}
原因是什么:
委员会没时间用完了吗?
多态lambda太难实现了?
或者PTB可能认为不需要它们?
注意:请记住上面的示例并不是唯一的例子,它仅作为代码类型的指南提供。仅专注于为上述代码提供解决方法的答案将不被视为有效!
相关来源:
答案 0 :(得分:51)
我们没有多态lambda的原因在this posting中得到了很好的解释。
它与从C ++ 11中提取的概念特性有关:基本上,多态lambda是普通的,无约束的函数模板,我们不知道如何使用无约束模板对概念约束模板进行类型检查。但是,解决这个问题变得很简单,如here所示(死链接),所以我认为没有任何障碍。
cpp-next的链接已经死了;可以找到相关信息here
答案 1 :(得分:16)
由于参数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,我知道你不喜欢这个答案,因为它没有回答你的问题,但是我愿意打赌下一个提出这个问题的人会去寻找一种使代码工作的方法,因此在问题相关的地方使用一些技术是有意义的。)
答案 2 :(得分:7)
这可能是因为已经有了这样做的语法,而lambdas的目的是引入一个更简单的语法来涵盖大多数情况。当你试图涵盖所有情况时(如果你想让自动生成的仿函数继承特定的基类怎么办?),你将失去lambda的比较优势(简洁性和简洁性)。
我真的不喜欢提出的语法。 T
是关键字吗?所有名称查找失败的标识符是否会自动转换为模板typename参数?这可以防止你发现拼写错误,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
推断出一个typename模板参数,而不是模板模板参数。在这种情况下无法进行类型推断。
在当前提案中, 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
的模板类型参数。在这方面,Lambdas与使用现有的仿函数没什么不同。
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; }
似乎没问题,但我觉得语法有误导性。语法建议从第一个实际参数推断出type参数,并且第二个参数被强制转换为相同类型,但实际上两个实际参数在类型推断期间被认为是相等的。
也许最好将这种情况局限于每个类型参数的一个lambda参数,如果你想要更多约束,可以自己编写仿函数。在我看来,这是灵活性和功能之间的良好折衷,而不是保持语法简单。
答案 3 :(得分:3)
好吧,既然你已经联系了n1968,那么你的问题的答案就显而易见了。它可以在提案的第5.1节中找到。
答案 4 :(得分:-2)
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。任何人都有任何疯狂的想法吗?)