我想在下面的代码中填写store()
和launch()
方法。捕获问题精神的重要细节是,foo
中声明的对象main()
在我们调用launch()
时不再存在。我怎么能这样做?
#include <cstdio>
#include <cstring>
#include <type_traits>
template<typename T, typename U=
typename std::enable_if<std::is_trivially_copyable<T>::value,T>::type>
struct Launchable {
void launch() { /* some code here */ }
T t;
// other members as needed to support DelayedLauncher
};
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
// copy-construct/memcpy t into some storage
}
void launch() const {
// call t.launch(), where t is (a copy of) the last value passed into store()
}
// other members as needed
};
int main() {
DelayedLauncher launcher;
{
Launchable<int> foo;
launcher.store(foo);
}
launcher.launch(); // calls foo.launch()
return 0;
}
请注意,如果我们只有一组固定的N种类型可以传入store()
,我们可以通过声明N Launchable<T>
个字段和N个非模板store()
方法来实现所需的功能,每个类型一个,以及一个枚举字段,其值在switch
方法的launch()
语句中使用。但我正在寻找DelayedLauncher
的实现,因为添加了更多Launchable
类型,所以不需要修改。
答案 0 :(得分:2)
使用std::function
:
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
f = [t]() {t.launch();};
}
void launch() const { f(); }
private:
std::function<void()> f;
};
答案 1 :(得分:1)
您可以为Launchable
提供一个具有虚拟launch()
且没有模板的基类,并在Launcher::store
中存储指向该基类的指针。
编辑:改编自@ dshin的解决方案:
struct LaunchableBase {
virtual void launch() = 0;
};
template<typename T, typename U=
typename std::enable_if<std::is_trivially_copyable<T>::value,T>::type>
struct Launchable : public LaunchableBase {
virtual void launch() override { /* some code here */ }
T t;
// other members as needed to support DelayedLauncher
};
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
static_assert(sizeof(t) <= sizeof(obj_buffer),
"insufficient obj_buffer size");
static_assert(std::is_trivially_destructible<T>::value,
"leak would occur with current impl");
p = new (obj_buffer) Launchable<T>(t);
}
void launch() const {
p->launch();
}
private:
char obj_buffer[1024]; // static_assert inside store() protects us from overflow
LaunchableBase *p;
};
答案 2 :(得分:0)
我相信Jarod42 solution的这种变体会避免动态分配,但如果有人能够确认这将按照我认为的方式运行,我将不胜感激:
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
static_assert(sizeof(t) <= sizeof(obj_buffer),
"insufficient obj_buffer size");
static_assert(std::is_trivially_destructible<T>::value,
"leak would occur with current impl");
auto p = new (obj_buffer) Launchable<T>(t);
auto ref = std::ref(*p);
f = [=]() {ref.get().launch();};
}
void launch() const {
f();
}
private:
char obj_buffer[1024]; // static_assert inside store() protects us from overflow
std::function<void()> f;
};
我认为它应该有效,因为我所看到的资源表明std::function
实现通常具有“小捕获”优化,只有在捕获数据的总大小超过某个阈值时才触发动态分配。
编辑:我在评论中用Jarod42提供的版本替换了我的代码。该标准保证上述实现不会触发动态分配。