采取可能具有以下接口(interface)的“惰性”构造函数:
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)...};
}
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