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;
};