我有相当数量的代码,它依赖于捕获shared_from_this()当使用lambda表达式作为回调时确保我的实例保持活动状态:
std::shared_ptr<Thing> self = shared_from_this();
auto doSomething = [this, self] ()
{
// various statements, none of which reference self, but do use this
}
Run Code Online (Sandbox Code Playgroud)
所以问题是:由于我没有self在lambda体内引用,是否允许一致的编译器优化捕获?
考虑以下程序:
#include <functional>
#include <iostream>
#include <memory>
std::function<void ()> gFunc;
struct S : std::enable_shared_from_this<S>
{
void putGlobal()
{
auto self = shared_from_this();
gFunc = [self] { };
}
};
int main()
{
auto x = std::make_shared<S>();
std::cout << x.use_count() << std::endl;
x->putGlobal();
std::cout << x.use_count() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
1 …Run Code Online (Sandbox Code Playgroud) std::function<int()> void f1()
{
int a, b, c, d, ..., x, y, z;
return [=] { return a + b + c; };
}
Run Code Online (Sandbox Code Playgroud)
与
std::function<int()> void f2()
{
int a, b, c, d, ..., x, y, z;
return [a, b, c] { return a + b + c; };
}
Run Code Online (Sandbox Code Playgroud)
毋庸置疑,前者比后者更短,更方便,更优雅.
但是,我仍然担心:
从性能的角度来看,后者总是优于前者吗?
标准是否保证lambda表达式仅捕获必要的变量?即,在前一示例中,仅捕获a,b,c,未使用的变量d,...,x,y,z不是.
假设复制变量具有所需的副作用.我想声明一个复制变量但不使用该变量的lambda.这样做的最低要求是什么?
Copiable copyable;
auto lambda1 = [=](){};
auto lambda2 = [copyable](){};
auto lambda3 = [=](){ copyable; }
auto lambda4 = [=](){ volatile copy = copyable; }
Run Code Online (Sandbox Code Playgroud)
lambda1使用隐式捕获,并且由于正文没有提及copyable,我不相信它实际上是复制它.
lambda2使用显式捕获,似乎根据这个,它应该通过副本捕获.是否允许编译器删除副本?有关此问题的另一个讨论,请参阅此
lambda3使用隐式捕获,但身体提到copyable.这是否构成了一种使用方式copyable?
lambda4使用隐式捕获并强制另一个volatile副本.我确信这实际上会有效,但它的副本数量超过了最低限度.
激励案例:我需要在完成任意数量的lambda调用后运行清理,可能在不同的线程中.我可以通过使用一个std::shared_ptr运行清理的自定义删除器来实现这一点,并以某种方式将其传递给每个lambda.然后,当所有共享ptrs超出范围时,清理将运行.
编辑:lambda3并且lambda4错过了=隐式捕获.
有时我会std::find_if在本地函数中执行(例如)具有5个局部变量(包括参数)的情况.但是,传入STL算法的lambda只需要访问其中的1个.我可以通过以下两种方式之一来捕获它:
void foo(int one, int two, int three)
{
std::vector<int> m_numbers;
int four, five;
std::find_if(m_numbers.begin(), m_numbers.end(), [=](int number) {
return number == four;
});
}
Run Code Online (Sandbox Code Playgroud)
或者我可以这样做:
void foo(int one, int two, int three)
{
std::vector<int> m_numbers;
int four, five;
std::find_if(m_numbers.begin(), m_numbers.end(), [four](int number) {
return number == four;
});
}
Run Code Online (Sandbox Code Playgroud)
(注意我没有编译此代码,对任何语法错误或其他错误道歉)
我知道隐式捕获是基于odr使用的规则,所以在功能和实现方面,我认为两者都是相同的.什么时候使用显式捕获而不是隐式捕获?我唯一的想法与封装原则有些相关:只能访问所需的东西,编译器可以帮助您确定何时访问变量.它还保持方法的本地状态(它的不变量,在执行期间函数的生命周期)更安全.但这些真的是实际问题吗?
是否存在使用显式捕获而不是隐式捕获的功能性原因?遵循什么是好的经验法则或最佳做法?
这一页检查并给出了如何动态加载和使用类的非常清晰的示例,但有一些我很难理解的内容:
我明白为什么需要“创建”功能,但为什么需要“销毁”功能?为什么没有将接口析构函数声明为纯虚拟函数?
我做了一个相同的例子,除了:
~polygon() = 0;
Run Code Online (Sandbox Code Playgroud)
的析构函数triangle是:
triangle::~triangle() {
std::cout << "triangle Dtor is called" <<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
然后当我使用时:
delete poly;
Run Code Online (Sandbox Code Playgroud)
该消息确实显示(Linux 下的 GCC 5.4.0)。
我试图寻找其他示例,但它们都提到并使用“销毁”函数,没有使用简单的纯虚拟析构函数的示例,这使我相信我在这里遗漏了一些东西,所以..它是什么?
不想使用销毁函数的背景是我想在 a 中使用分配的对象shared_ptr并且以后不关心它的生命周期,使用“销毁”函数会很棘手,因此我需要知道是否有必要。