带有转发的延迟初始化
lazy initialization with 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;
};
实现这一点的好方法是什么?
这里有一个复杂的方法来做你想做的事情。其基本思想是让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(this或this)上有几个示例显示了如何实现它。
最后用辅助函数模板构造LazyConstruct
实例
template<class T, class... Args>
LazyConstruct<T, Args...> make_LazyConstruct(Args&&... args)
{
return LazyConstruct<T, Args...>{std::forward<Args>(args)...};
}
现场演示
另一个基于Alf答案的版本使用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;
};
实时演示
我不确定你的问题,但对于延迟初始化,我建议你使用类似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.可选
最简单的方法可能只是捕获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;
};
免责声明:代码不被编译器的手触摸。
注意:虽然我现在还不能确切地说出这个想法出了什么问题(已经很晚了),但不知何故,懒惰的创作闻起来并不对劲。我怀疑优化过早。
根据前面的注释。您希望延迟并捕获参数。
编辑:通用解决方案,应该在C++11中工作。警告:未测试。应用函数作为练习。请参阅此处了解可能的实现:
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();
}
相关文章:
- 模板化类中静态成员的延迟初始化
- 我的子类的属性是延迟初始化的
- 在声明或实现中延迟初始化C++单一实例
- 使用 lambda 和可变参数模板延迟初始化对象 - 任意传递值
- C++11:对延迟初始化进行安全的双重检查锁定.可能的
- 本地静态对象的非延迟初始化
- 私有类成员延迟初始化的最佳做法
- 无锁代码中的延迟初始化
- 模板和延迟初始化
- 带有转发的延迟初始化
- 有没有一种方法可以延迟初始化类的静态子对象
- 堆栈对象的延迟初始化
- c++类成员和延迟初始化
- 延迟初始化缓存..我如何使它线程安全
- 延迟初始化
- 延迟初始化要初始化的对象
- 如何实现RAII +延迟初始化
- singleton的延迟初始化问题
- c++ 11中unique_ptr的延迟初始化
- std::unique_ptr的延迟初始化