延迟初始化与转发

时间:2014-10-22 03:25:52

标签: c++ c++11 perfect-forwarding

使用可能具有以下界面的“懒惰”构造函数:

template<class T>
struct LazyConstruct {
   // accept any number of arguments, 
   // which would later be used to construct T
   template<class... U>
   LazyConstruct(U&&... u) {
       // store the arguments somehow
   }
   T& get() {
      if(!data) data.reset( new T( /* unpack the arguments */ ) );
      return *data;
   }
private:
   std::unique_ptr<T> data;
};

实现这个的好方法是什么?

4 个答案:

答案 0 :(得分:1)

我不确定你的问题,但是对于懒惰的初始化,我建议你使用boost::optional<T>的内容。您可以使用它来延迟初始化,并且不会使用指针和堆内存。

class MyClass {
public:
    void f();
};

void anotherFunc(MyClass & c);

boost::optional<MyClass> mc; //Not initialized, empty, stack memory.

mc = MyClass{};
if (mc != boost::none)
    mc->f();
anotherFunc(*mc);

文档在这里:Boost.Optional

答案 1 :(得分:1)

最简单的可能就是捕获lambda中的参数。

template<class T>
struct LazyConstruct {
   // accept any number of arguments, 
   // which would later be used to construct T
   template<class... U>
   LazyConstruct(U&&... u)
       : create( [=]() -> T* { return new T(u...); } ) 
   {}
   T& get() {
      if(!data) data.reset( data.reset( create() ) );
      return *data;
   }
private:
   std::unique_ptr<T> data;
   std::function<auto()->T*> create;
};

免责声明:编译人员未触及代码。

注意:虽然我现在无法确切地说出这个想法究竟有什么不对(这已经很晚了),但是懒惰的创作并不能以某种方式闻到。我怀疑过早优化。

答案 2 :(得分:1)

这是一种令人费解的做事你想要的方式。基本思路是让LazyConstruct将参数包存储在tuple中,然后根据需要解包tuple以构建T

template<class T, class... Args>
struct LazyConstruct {
   // accept any number of arguments, 
   // which would later be used to construct T
   template<class... U>
   LazyConstruct(U&&... u)
   : args(std::make_tuple(std::forward<U>(u)...))
   {
   }

   T& get() {
      if(!data) data = create(std::index_sequence_for<Args...>());
      return *data;
   }

   template<std::size_t... I>
   std::unique_ptr<T> create(std::index_sequence<I...>)
   {
      return std::unique_ptr<T>{new T(std::get<I>(args)...)};
   }

private:
   std::tuple<typename std::decay<Args>::type...> args;
   std::unique_ptr<T> data;
};

我正在使用C ++ 14的std::index_sequence,如果您的标准库实现没有提供,那么SO(thisthis)上有几个示例如何实施。

最后一个辅助函数模板来构造LazyConstruct实例

template<class T, class... Args>
LazyConstruct<T, Args...> make_LazyConstruct(Args&&... args)
{
    return LazyConstruct<T, Args...>{std::forward<Args>(args)...};
}

Live demo


另一个基于Alf's answer的版本使用std::function,因此LazyConstruct的类型不会因T的构造函数签名而发生变化。

template<class T>
struct LazyConstruct {
   template<class... Args>
   LazyConstruct(Args&&... args)
   : holder([this, args = std::make_tuple(std::forward<Args>(args)...)]() {
            return create(std::index_sequence_for<Args...>(), std::move(args));
       })
   {
   }

   T& get() {
      if(!data) data = holder();
      return *data;
   }

   template<std::size_t... I, class Tuple>
   std::unique_ptr<T> create(std::index_sequence<I...>, Tuple args)
   {
      return std::unique_ptr<T>{new T(std::get<I>(args)...)};
   }

private:
   std::function<std::unique_ptr<T>()> holder;
   std::unique_ptr<T> data;
};

Live demo

答案 3 :(得分:0)

根据之前的评论。你想延迟并捕获参数。

编辑:通用解决方案,应该在C ++ 11中工作。警告:未经测试。应用功能留作练习。有关可能的实施,请参阅here

template <class T>
struct make {
    template <class...Args>
    T operator()(Args &&... args) const {
        return T(std::forward<Args>(args)...);
  }
};


template <class T, class... Args>
struct object_builder {

    object_builder(Args... && args) :
        captured_args_(std::forward<Args>(args)...) {}

    T operator()() const {
        return apply(make<T>{},
                 captured_args_);
}

 private:
    std::tuple<Args...> captured_args_;
};


template <class T, class...Args>
object_builder<T, Args...> make_object_builder(Args &&...args) {
    return object_builder<T, Args...>(std::forward<Args>(args)...);
}

int main() {
    //Create builders with captured arguments
    auto scary_monster_builder = 
        make_object_builder<Monster>(scary, "big orc");
    auto easy_monster_builder = make_object_builder<Monster>(easy,
                                                         "small orc");

    //Instantiate objects with the captured arguments from before
    auto a_scary_monster = scary_monster_builder();
    auto an_easy_monster = easy_monster_builder();
}