使用可变参数模板延迟 make_unique
Deferred make_unique with variadic templates
我需要一个 class,它将作为延迟工厂工作,保存参数以创建另一个 class 并稍后及时调用 make_unique。到目前为止,我没有任何运气让可变模板版本工作。任何帮助将不胜感激(下面是最小的非工作版本)。
template <typename T, typename ... Args>
class ConstructLater
{
public:
ConstructLater(Args &&... args)
{
factory = std::bind(std::make_unique<T, Args...>, std::forward<Args>(args)...);
}
std::unique_ptr<T> Later()
{
return factory();
}
private:
std::function<std::unique_ptr<T>(void)> factory;
};
class Foo { public: Foo(int) { } };
int f()
{
// None of these work
ConstructLater<Foo>(3);
ConstructLater<Foo, int>(6);
}
编辑:澄清一下,我不需要使用 std::bind,事实上我希望 class 按值存储参数的副本以避免临时对象出现问题。还将标签更新为 C++14。
您可以使用 lambda,但它是未定义的行为,因为您正在保存对临时的引用
factory = [&]() {
return std::make_unique<T>(std::forward<Args>(args)...);
};
为了保存参数并保留调用它们的原始类型,它更复杂并且涉及很多 casts/variadic 模板魔术。我还没有弄清楚,但不明白为什么这是不可能的。
template<class... Args>
ConstructLater(Args &&... args)
{
std::tuple<TODO...> tup = remove_const_remove_ref<Args...>(args);
factory = [=]() {
return std::make_unique<T>(cast_tuple<Args...>(tup)...);
};
}
这是std::make_unique
的签名
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&...);
当您像您一样指定 Args
时,std::make_unique
可能只接受右值。但是,std::bind
将其绑定参数作为左值传递,这就是您收到错误的原因。
使用 lambda 代替 std::bind
template<typename...>
struct pack {};
template<typename T, typename Tup, typename... TArgs, std::size_t... Is>
std::unique_ptr<T> helper(Tup&& tup, pack<TArgs...>, std::index_sequence<Is...>)
{
return std::make_unique<T>(static_cast<TArgs>(std::get<Is>(tup))...);
}
template <typename T>
class ConstructLater
{
public:
template<typename... Args>
ConstructLater(Args&&... args)
: factory{[tup = std::make_tuple(std::forward<Args>(args)...)]() mutable {
return helper<T>(std::move(tup), pack<Args&&...>{}, std::index_sequence_for<Args...>{});
}}
{
}
std::unique_ptr<T> Later()
{
return factory();
}
private:
std::function<std::unique_ptr<T>(void)> factory;
};
还请注意 ConstructLater
不应该由 Args
模板化,它违背了 std::function
的全部目的。无论哪种方式,构造函数本身都需要模板化才能完美地转发其参数。
如果您同意复制每个参数,那么您可以跳过 std::bind
和 std::make_unique
:
template <typename T, typename ... Args>
class ConstructLater
{
public:
ConstructLater(Args... args) : _storedArgs(args...)
{
}
std::unique_ptr<T> later()
{
return laterHelper(std::make_index_sequence<sizeof...(Args)>{});
}
private:
template<std::size_t... I>
std::unique_ptr<T> laterHelper(std::index_sequence<I...>)
{
return std::unique_ptr<T>(new T(std::get<I>(_storedArgs)...));
}
std::tuple<Args...> _storedArgs;
};
class Foo { public: Foo(int) { } };
int f()
{
ConstructLater<Foo, int> cl(6);
auto foo = cl.later();
}
这在 C++14 中编译得很好:https://godbolt.org/z/owgoXc
我需要一个 class,它将作为延迟工厂工作,保存参数以创建另一个 class 并稍后及时调用 make_unique。到目前为止,我没有任何运气让可变模板版本工作。任何帮助将不胜感激(下面是最小的非工作版本)。
template <typename T, typename ... Args>
class ConstructLater
{
public:
ConstructLater(Args &&... args)
{
factory = std::bind(std::make_unique<T, Args...>, std::forward<Args>(args)...);
}
std::unique_ptr<T> Later()
{
return factory();
}
private:
std::function<std::unique_ptr<T>(void)> factory;
};
class Foo { public: Foo(int) { } };
int f()
{
// None of these work
ConstructLater<Foo>(3);
ConstructLater<Foo, int>(6);
}
编辑:澄清一下,我不需要使用 std::bind,事实上我希望 class 按值存储参数的副本以避免临时对象出现问题。还将标签更新为 C++14。
您可以使用 lambda,但它是未定义的行为,因为您正在保存对临时的引用
factory = [&]() {
return std::make_unique<T>(std::forward<Args>(args)...);
};
为了保存参数并保留调用它们的原始类型,它更复杂并且涉及很多 casts/variadic 模板魔术。我还没有弄清楚,但不明白为什么这是不可能的。
template<class... Args>
ConstructLater(Args &&... args)
{
std::tuple<TODO...> tup = remove_const_remove_ref<Args...>(args);
factory = [=]() {
return std::make_unique<T>(cast_tuple<Args...>(tup)...);
};
}
这是std::make_unique
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&...);
当您像您一样指定 Args
时,std::make_unique
可能只接受右值。但是,std::bind
将其绑定参数作为左值传递,这就是您收到错误的原因。
使用 lambda 代替 std::bind
template<typename...>
struct pack {};
template<typename T, typename Tup, typename... TArgs, std::size_t... Is>
std::unique_ptr<T> helper(Tup&& tup, pack<TArgs...>, std::index_sequence<Is...>)
{
return std::make_unique<T>(static_cast<TArgs>(std::get<Is>(tup))...);
}
template <typename T>
class ConstructLater
{
public:
template<typename... Args>
ConstructLater(Args&&... args)
: factory{[tup = std::make_tuple(std::forward<Args>(args)...)]() mutable {
return helper<T>(std::move(tup), pack<Args&&...>{}, std::index_sequence_for<Args...>{});
}}
{
}
std::unique_ptr<T> Later()
{
return factory();
}
private:
std::function<std::unique_ptr<T>(void)> factory;
};
还请注意 ConstructLater
不应该由 Args
模板化,它违背了 std::function
的全部目的。无论哪种方式,构造函数本身都需要模板化才能完美地转发其参数。
如果您同意复制每个参数,那么您可以跳过 std::bind
和 std::make_unique
:
template <typename T, typename ... Args>
class ConstructLater
{
public:
ConstructLater(Args... args) : _storedArgs(args...)
{
}
std::unique_ptr<T> later()
{
return laterHelper(std::make_index_sequence<sizeof...(Args)>{});
}
private:
template<std::size_t... I>
std::unique_ptr<T> laterHelper(std::index_sequence<I...>)
{
return std::unique_ptr<T>(new T(std::get<I>(_storedArgs)...));
}
std::tuple<Args...> _storedArgs;
};
class Foo { public: Foo(int) { } };
int f()
{
ConstructLater<Foo, int> cl(6);
auto foo = cl.later();
}
这在 C++14 中编译得很好:https://godbolt.org/z/owgoXc