我一直在考虑最近存储C++ lambda.您在Internet上看到的标准建议是将lambda存储在std :: function对象中.但是,这些建议都没有考虑存储的影响.在我看来,必须在幕后进行一些严肃的伏都教才能完成这项工作.考虑以下存储整数值的类:
class Simple {
public:
Simple( int value ) { puts( "Constructing simple!" ); this->value = value; }
Simple( const Simple& rhs ) { puts( "Copying simple!" ); this->value = rhs.value; }
Simple( Simple&& rhs ) { puts( "Moving simple!" ); this->value = rhs.value; }
~Simple() { puts( "Destroying simple!" ); }
int Get() const { return this->value; }
private:
int value;
};
Run Code Online (Sandbox Code Playgroud)
现在,考虑这个简单的程序:
int main()
{
Simple test( 5 );
std::function<int ()> f =
[test] ()
{
return test.Get();
};
printf( "%d\n", f() );
}
Run Code Online (Sandbox Code Playgroud)
这是我希望从这个程序中看到的输出:
Constructing simple!
Copying simple!
Moving simple!
Destroying simple!
5
Destroying simple!
Destroying simple!
Run Code Online (Sandbox Code Playgroud)
首先,我们创建价值测试.我们在堆栈上为临时lambda对象创建一个本地副本.然后我们将临时lambda对象移动到由std :: function分配的内存中.我们摧毁了临时的lambda.我们打印输出.我们破坏了std :: function.最后,我们销毁测试对象.
不用说,这不是我所看到的.当我在Visual C++ 2010(发布或调试模式)上编译它时,我得到此输出:
Constructing simple!
Copying simple!
Copying simple!
Copying simple!
Copying simple!
Destroying simple!
Destroying simple!
Destroying simple!
5
Destroying simple!
Destroying simple!
Run Code Online (Sandbox Code Playgroud)
神圣的废话是低效的!编译器不仅没有使用我的移动构造函数,而且在赋值期间它生成并销毁了两个明显多余的lambda副本.
所以,最后这里是问题:(1)所有这些复制真的有必要吗?(2)有没有办法强制编译器生成更好的代码?谢谢阅读!
Nic*_*las 10
您在Internet上看到的标准建议是将lambda存储在std :: function对象中.但是,这些建议都没有考虑存储的影响.
那是因为没关系.您无权访问lambda的类型名称.因此,虽然您auto
最初可以将其存储在其本机类型中,但它不会将该范围保留为该类型.您无法将其作为该类型返回.你只能把它粘在其他东西上.而C++ 11提供的唯一"其他东西"就是std::function
.
所以你有一个选择:暂时坚持auto
,锁定在该范围内.或者将其粘在std::function
长期存放中.
所有这些复制真的有必要吗?
技术上?不,没有必要做什么std::function
.
有没有办法强制编译器生成更好的代码?
不.这不是编译器的错; 这就是这个特定的实现方式std::function
.它可以减少复制; 它不应该复制超过两次(并且取决于编译器如何生成lambda,可能只有一次).但确实如此.
前一段时间我用MSVC10注意到了同样的性能问题,并在microsoft connect上提交了一个错误报告:https://connect.microsoft.com/VisualStudio/feedback/details/649268/std-bind-and-std-function-generate-
一个疯狂的用户号码的副本#细节
该错误被关闭为"固定".使用MSVC11开发人员预览您的代码现在确实打印:
Constructing simple!
Copying simple!
Moving simple!
Destroying simple!
5
Destroying simple!
Destroying simple!
Run Code Online (Sandbox Code Playgroud)
你的第一个问题就是MSVC的实现std::function
效率低下.使用g ++ 4.5.1,我得到:
Constructing simple!
Copying simple!
Moving simple!
Destroying simple!
5
Destroying simple!
Destroying simple!
Run Code Online (Sandbox Code Playgroud)
那仍然是创造一个额外的副本.问题是你的lambda是test
按值捕获的,这就是你拥有所有副本的原因.尝试:
int main()
{
Simple test( 5 );
std::function<int ()> f =
[&test] () // <-- Note added &
{
return test.Get();
};
printf( "%d\n", f() );
}
Run Code Online (Sandbox Code Playgroud)
Constructing simple!
5
Destroying simple!
Run Code Online (Sandbox Code Playgroud)
请注意,如果通过引用捕获,则必须确保在test
生命周期内保持活动状态f
,否则您将使用对已销毁对象的引用,这会引发未定义的行为.如果f
需要寿命更长,test
那么你必须使用pass by value版本.
归档时间: |
|
查看次数: |
6464 次 |
最近记录: |