在本文中,它说下面的代码是有效的C++ 11,并与GNU的libstdc ++一起使用:
int n; std::vectorv; ... std::function f(std::cref([n](int i) {return i%n == 0)); std::count_if(v.begin(), v.end(), f);
问题是我离开了相信要在调用站点创建的lambda对象,什么会使它成为此片段中的临时对象,因为它不存储在任何变量上,而是const
创建并传递给它的引用这个std::function
.如果是这样的话,lambda对象应该一直被销毁,留下一个悬空引用f
,这将导致使用时的未定义行为std::count_if
.
假设文章没有错,我的心理模型有什么问题?当lambda对象被破坏时?
好吧,让我们从基础开始:上面的代码当然不合法,因为它在一些相当基本的方面是不正确的.这条线
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
至少需要写成
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0;}));
请注意,代码是在Dr.Dobb的文章中写的,因为它在问题中,即任何合法的代码声明已经非常值得怀疑.
一旦解决了简单的语法错误,下一个问题是是否std::cref()
可以实际用于绑定到右值.根据5.1.2 [expr.prim.lambda]第2段,lambda exrpession显然是暂时的(感谢DyP作为参考).因为将引用绑定到临时并且在其他地方被禁止通常是一个相当糟糕的主意,std::cref()
这将是绕过这种限制的一种方法.事实证明,根据20.10 [function.objects]第2段std::cref()
被宣布为
template <class T> reference_wrapper<const T> cref(const T&) noexcept; template <class T> void cref(const T&&) = delete; template <class T> reference_wrapper<const T> cref(reference_wrapper<T>) noexcept;
也就是说,即使在纠正语法错误之后,语句也是不正确的.gcc和clang都没有编译这个代码(我已经使用了两个编译器的相当最新版本及其各自的标准C++库).也就是说,基于上述声明,此代码显然是非法的!
最后,在上述表达中没有什么可以延长临时的生命周期.临时扩展的生命周期的唯一原因是它或其中一个数据成员立即绑定到[ const
]引用.在临时函数周围包含函数调用会禁止此生命周期扩展.
总结:文章中引用的代码在许多不同的层面上都是不合法的!
我是上述文章的作者,我为自己的错误道歉.在这种情况下,没有人应该受到指责.我只是要求编辑添加一个勘误表:
1)更换
std::count_if(v.begin(), v.end(), std::cref(is_multiple_of(n)));
同
is_multiple_of f(n); std::count_if(v.begin(), v.end(), std::cref(f));
2)更换
std::count_if(v.begin(), v.end(), std::cref([n](int i){return i%n == 0;}));
同
auto f([n](int i){return i%n == 0;}); std::count_if(v.begin(), v.end(), std::cref(f));
3)更换
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
同
auto f1([n](int i){return i%n == 0;}); std::function<bool(int)> f(std::cref(f1));
在所有情况下问题都是一样的(就像DietmarKühl很好地解释的那样,给他+1):我们呼吁std::cref
暂时的.此函数返回std::reference_wrapper
存储指向临时的指针,如果std::reference_wrapper
超过临时值,则此指针将悬空.基本上这是在上面的案例3中发生的事情(其中也包含拼写错误).
在案例1和案例2中,std::reference_wrapper
不会比临时案件更长寿.但是,由于std::cref
删除了接受临时值(rvalues)的重载,代码不应该编译(包括情况3).在发布时,实施与标准不同,如今.用于编译的代码,但在与标准库的较新实现一起使用时不会.这不是我错误的借口.
在任何情况下,我相信文章的主要观点,即,使用 std::reference_wrapper
,std::cref
并std::ref
避免昂贵的副本,并动态分配仍然有效提供,当然,该引用的对象的寿命足够长的时间.
我再次为此带来的不便表示歉意.
更新:该文章已得到修复.感谢uk4321,DyP,特别是lvella和DietmarKühl提出并讨论了这个问题.