捕获可变参数模板化参数的模板参数

Capture template parameter of variadic templated arguments

本文关键字:参数 变参      更新时间:2023-10-16

我有以下(不完整,不起作用(定义:

template<typename T, std::function<Args(Context&)>... Funcs>
struct constructor
{
    T construct(Context& ctx)
    {
        return T(Funcs(ctx)...);
    }
};

我想要的是一个模板化类 - 第一个参数是构造类型,以下都是要调用的函数,带有 std::function s 的用户模板,然后调用这些模板以生成类型 T 的构造函数的值。

我认为没有可能使此代码工作,除了捕获函数的返回类型之外。我希望用户像这样使用它:

std::function<int(Context&)> ind = [](Context&) {return 2;};
Constructor<int, ind> c;
// c.construct(...) returns 2 by calling the constructor int(int) with argument
//                      ind(ctx) - which returns 2.

这可能大致是您要查找的内容。请记住,std::function不能是模板参数。

template <typename R> using Generator = std::function<R (Context&)>;
template <typename T, typename Generators, std::size_t... Is>
T constructImpl(Context& ctx, const Generators& generators,
                std::index_sequence<Is...>) {
    return T(std::get<Is>(generators)(ctx)...);
}
template <typename T, typename... Args>
class Constructor {
   std::tuple<Generator<Args>...> generators;
public:
   Constructor(Generator<Args>... generators)
       : generators(std::move(generators)...)
   {}
   T construct(Context& ctx) {
       return constructImpl<T>(ctx, generators,
                               std::index_sequence_for<Args...>());
   }
};

用法:

Constructor<int, int> c([](Context&) { return 2; });
int i = c.construct(context);
assert(i == 2);

类型不能依赖于运行时数据。

调用std::function<X(Y)>需要运行时数据。 所以你的类型不能依赖于std::function<X(Y)>,所以类型不能用作模板参数。

现在,它可以依赖于指向全局对象的指针:有趣的是,就C++而言,这不是运行时状态。

因此,您的设计从根本上是有缺陷的。

如果你想要一个返回 2 的函数,这有效:

template<class...ignored>
struct Constructor {
  template<class... also_ignored>
  Constructor(also_ignored&&...) {}
  template<class... also_ignored>
  int construct(also_ignored&&...) { return 2; }
};

这将通过您的 OP 中描述的单元测试,但您无法通过 ind 传递给Constructor因为它不合法。 但是,将其从类型签名中删除并不重要。

如果您想要更多功率,我们可以这样做:

template<class T, class... Functors>
struct Constructor {
  T construct( Context& ctx ) {
    return T( Functors{}( ctx )... );
  }
};

在这种情况下,您需要无状态函数对象:

struct ind { int operator()(Context&)const{return 2;} };

就像std::map需要无状态比较对象一样。

如果您的函数对象需要状态,那么您需要存储它们的副本以供Constructor访问(可能在 Constructor 内(,并且您可能需要元组和索引技巧来存储它们。 ("索引技巧"是一个有用的谷歌(

我认为你的Construct可以只是一个函数:

template <typename T, typename... Funcs>
T construct(Context& ctx, Funcs... funcs) {
    return T(funcs(ctx)...);
}

在您的示例中,其用法可能是:

int x = construct<int>(ctx, [](Context& ) { return 2; });