使用可变参数模板延迟 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::bindstd::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