复制构造并稍后访问任意POD类型

时间:2016-01-01 20:57:58

标签: c++ templates

我想在下面的代码中填写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类型,所以不需要修改。

3 个答案:

答案 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提供的版本替换了我的代码。该标准保证上述实现不会触发动态分配。