lambda mutable的慎用及二进制膨胀
2021年12月30日
字数统计:991
这篇文章的灵感来源于我加入的两个C++律师群,起因分别是滥用mutable和关于二进制膨胀的讨论。
慎用mutable
首先参考如下代码:
#include <string>
#include <iostream>
#include <algorithm>
int main () {
std :: string str2 = "Text with some whitespaces" ;
int i {};
std :: cout << str2 << std :: endl ;
str2 . erase ( std :: remove_if ( str2 . begin (),
str2 . end (), [ i ]( unsigned char x ) mutable {
if ( ! i && std :: isspace ( x )) { i = 1 ; return 1 ; }
else return 0 ; }));
std :: cout << str2 . c_str () << std :: endl ;
}
std::remove_ if的作用是返回满足条件的容器的成员的迭代器,这个可变lambda通过捕获一个i使得这个lambda只能返回一次1(true)。
整个算法的意思是删除字符串中的第一个空格。
一切看似那么的美好,直到你将它运行起来,就会发现算法实际上删除了两个空格而不是一个。
即使你绞尽脑汁,也不一定想出这是为什么,实际上这是因为lambda自身的性质外加错误使用造成了未定义行为。
首先来看std::remove_ if的实现:
template < class ForwardIt , class UnaryPredicate >
ForwardIt remove_if ( ForwardIt first , ForwardIt last , UnaryPredicate p )
{
first = std :: find_if ( first , last , p );
if ( first != last )
for ( ForwardIt i = first ; ++ i != last ; )
if ( ! p ( * i ))
* first ++ = std :: move ( * i );
return first ;
}
会发现,被传入remove_ if的lambda在算法内又被传递给了find_ if这个算法。
由于 lambda实际上是一个类的对象 ,所以复制一个lambda实际上复制了一个对象。
在这种情况下,find_ if复制了一次lambda,造成两个lambda并不共享同一个i,于是两个i = 0对应空格删除了两次。
这种情况就属于对mutable的滥用,换句话说,C++语法和算法库的特性导致算法只有在接收const的lambda作为参数时才能做到正确。
防止lambda代码膨胀
在C++的争论中,模板导致的二进制膨胀一直饱受批评,但是模板自身其实做到了开销平衡,不过lambda的一些细节值得推敲。
问题:
#include <algorithm>
void foo ( int * f , int * l )
{
std :: sort ( f , l ,[]( int a , int b )
{
return a > b ;
});
}
void foo2 ( int * f , int * l )
{
std :: sort ( f , l ,[]( int a , int b )
{
return a > b ;
});
}
foo和foo2虽然代码一模一样,但是编译器仍然会生成两份lambda,所以将lambda抽象出来进行复用:
#include <algorithm>
auto const a = []( auto a , auto b )
{
return a > b ;
};
void foo ( int * f , int * l )
{
std :: sort ( f , l , a );
}
void foo2 ( int * f , int * l )
{
std :: sort ( f , l , a );
}
此时,lambda的二进制膨胀问题得到了初步的解决。
若无特殊声明,本人原创文章以
CC BY-SA 4.0许可协议
提供。
本站不欢迎非搜索引擎类,非个人学习类爬虫;严禁将文章直接爬取至其他站点。
若看到此条消息,说明你正在访问的网站可能是垃圾二手转载网站。
为了获得更好的浏览体验,请访问唯一原始网站:mysteriouspreserve[dot]com或blog[dot]bizwen[dot]com。