C++ 使用存储在可变数据结构中的字段作为方法参数

C++ 使用存储在可变数据结构中的字段作为方法参数,c++,c++11,recursion,variadic-templates,template-meta-programming,C++,C++11,Recursion,Variadic Templates,Template Meta Programming,我有一个可变的数据结构,每个“层”包含一个字段 如何将存储在结构中的所有字段用作函数或构造函数的参数 template <class... Ts> class Builder {}; template <class T, class... Ts> class Builder<T, Ts...> : public Builder<Ts...> { public: Builder(T t, Ts... ts) : Builder<Ts.

我有一个可变的数据结构,每个“层”包含一个字段

如何将存储在结构中的所有字段用作函数或构造函数的参数

template <class... Ts> class Builder {};

template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
    Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}

    Result build() {
      // want to use tail, Builder<Ts...>::tail, etc.
      // as ctor or function arguments without multiple specializations
    }

private:
    const T tail;
};
模板类生成器{};
模板
类生成器:公共生成器{
公众:
生成器(T,T…T):生成器(T…),尾部(T){
结果生成(){
//要使用tail、Builder::tail等。
//作为没有多个专门化的构造函数或函数参数
}
私人:
恒定尾;
};
总的来说,我希望能够做这样的事情:

Builder<int, string, int> b1{10, "aaa", 20};
Result r1 = b1.build(); // should invoke Result's constructor (int, string, int)

Builder<int> b2{10};
Result r2 = b2.build(); // should invoke Result's constructor (int)
builderb1{10,“aaa”,20};
结果r1=b1.build();//应调用结果的构造函数(int、string、int)
建筑商b2{10};
结果r2=b2.build();//应调用结果的构造函数(int)

我找到的解决方案之一是传递包含字段的中间
元组
,然后使用中描述的机制将其解压缩:

//解包帮助程序
模板结构seq{};
模板
结构gens:gens{};
模板
结构族{
typedef-seq-type;
};
//字段为0的生成器返回空元组
模板类生成器{
公众:
tuple compute_tuple(){
返回{};
}
};
模板
类生成器:公共生成器{
公众:
生成器(T,T…T):生成器(T…),尾部(T){
结果生成(){
//获取参数元组
自动参数=compute_tuple();
//使用参数元组作为结果的参数
递归返回build_(typename gens::type{},参数);
}
受保护的:
//计算元组-仅将当前元素与超类的结果联接
tuple compute_tuple(){
常量元组头{field};
const tuple tail=Builder::compute_tuple();
返回元组(头、尾);
}
私人:
模板
递归生成结果(序列、元组数据){
//调用匹配结果的构造函数
返回{std::get(data)…};
}
常数场;
};
然后它会正常工作:

Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
builderb1{“a”,“b”};
b1.build();//调用结果(字符串,字符串)

尽管如此,如果没有
tuple
中介,也许可以做一些更简单的事情?

我想您可以使用lambda(并将其保存在
std::function
中)来存储值

例如(注意:代码未测试)(感谢Oliv的更正)

模板
类生成器
{
私人:
std::功能fn;
公众:
生成器(Ts const&…Ts):fn{[=]{返回结果{Ts..};}
{ }
结果生成()
{返回fn();}
};

如果您不想使用tuple作为成员来保存值,可以这样做:

template <class... Ts> class Builder {
  protected:
  template<class...Us>
  Result do_build(const Us&...us){
    return Result(us...);
    }
  };

template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
    Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}

    Result build() {
      return do_build();
    }
protected:
  template<class...Us>
  Result do_build(const Us&...us){
    return Builder<Ts...>::do_build(us...,tail);
    }
private:
    const T tail;
};
模板类生成器{
受保护的:
模板
生成结果(const Us和…Us){
返回结果(美国…);
}
};
模板
类生成器:公共生成器{
公众:
生成器(T,T…T):生成器(T…),尾部(T){
结果生成(){
返回do_build();
}
受保护的:
模板
生成结果(const Us和…Us){
返回生成器::do_build(美国…,tail);
}
私人:
恒定尾;
};
模板结构生成器{
auto as_tie()常量{return std::tie();}
};
模板
结构生成器:生成器{
使用base=Builder;
自动作为常数{
返回std::tuple_cat(base::as_tie(),std::tie(tail));
}
现在可以将
Builder::as_tie()
传递到
std::apply
(或后端口版本)或
make_from_tuple

当然,
操作符T
技巧可以用于返回类型推断。但我通常建议不要使用它。

您可以使用
Idx
标记从
n
-th
生成器中获取
尾部

template<std::size_t i> struct Idx {};

template<class... Ts>
class Builder {
public:
    void get_tail();
};

template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
    static constexpr auto index = sizeof...(Ts);

public:
    Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
    }

    Result build() {
        return build_impl(std::make_index_sequence<index + 1>{});
    }

protected:
    using Builder<Ts...>::get_tail;

    const T& get_tail(Idx<index>) {
        return tail;
    }

private:
    template<std::size_t... is>
    Result build_impl(std::index_sequence<is...>) {
        return Result{get_tail(Idx<index - is>{})...};
    }

private:
    const T tail;
};
模板结构Idx{};
模板
类生成器{
公众:
void get_tail();
};
模板
类生成器:公共生成器{
私人:
静态constexpr auto index=sizeof…(Ts);
公众:
建造商(T,T…T):建造商(T…),尾部(T){
}
结果生成(){
返回build_impl(std::make_index_sequence{});
}
受保护的:
使用Builder::get_tail;
const T&get_tail(Idx){
返回尾;
}
私人:
模板
结果生成执行(标准::索引顺序){
返回结果{get_tail(Idx{})…};
}
私人:
恒定尾;
};

为什么不持有一个
std::tuple
并使用许多标准库实用程序来简化工作?不幸的是,构建器只是一个简化,在实际场景中,它是一个由多个子缓冲区组成的复合缓冲区,返回值(字段
tail
不存在)。该解决方案利用了
tail
字段。因此,如果该示例不具有指示性,并且该解决方案不适用,那么这里完成了什么?是否应该捕获ts…呢?那么所有这些值都将存储在堆中,对于这样一个小功能来说,这是非常昂贵的。@Oliv-当然:
Builder(ts&…ts):fn{[]{返回结果{ts..};}
;但这是危险的;如果一个构造
Builder
的值在
build()之前被销毁
调用时,相应的已保存引用是一个悬空引用。也许可以通过完美的转发来执行某些操作,但您必须模板化构造函数。您的代码格式不正确,必须捕获!并且为了不让UB参数被值捕获。
[=]
@Oliv-因此,在我看来,最好以Ts const形式接收参数&……;也许这样可以避免一些复制(但我怀疑编译器优化了这方面)。
template <class... Ts> class Builder {
  protected:
  template<class...Us>
  Result do_build(const Us&...us){
    return Result(us...);
    }
  };

template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
    Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}

    Result build() {
      return do_build();
    }
protected:
  template<class...Us>
  Result do_build(const Us&...us){
    return Builder<Ts...>::do_build(us...,tail);
    }
private:
    const T tail;
};
template <class... Ts>struct Builder {
  auto as_tie() const { return std::tie(); }
};

template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
  using base = Builder<Ts...>;
  auto as_tie()const{
    return std::tuple_cat( base::as_tie(), std::tie( tail ) );
  }
template<std::size_t i> struct Idx {};

template<class... Ts>
class Builder {
public:
    void get_tail();
};

template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
    static constexpr auto index = sizeof...(Ts);

public:
    Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
    }

    Result build() {
        return build_impl(std::make_index_sequence<index + 1>{});
    }

protected:
    using Builder<Ts...>::get_tail;

    const T& get_tail(Idx<index>) {
        return tail;
    }

private:
    template<std::size_t... is>
    Result build_impl(std::index_sequence<is...>) {
        return Result{get_tail(Idx<index - is>{})...};
    }

private:
    const T tail;
};