使用存储在可变数据结构中的字段作为方法参数
Using fields stored in variadic data structure as method arguments
我有一个可变数据结构,每个 "layer" 包含一个字段。
如何使用存储在结构中的所有字段作为函数或构造函数的参数?
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;
};
总的来说,我希望能够做这样的事情:
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)
我找到的解决方案之一是传递包含字段的中介 tuple<...>
,然后使用 "unpacking" a tuple to call a matching function pointer 中描述的机制将其解压缩:
// unpacking helpers
template<int ...> struct seq {};
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> {};
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
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() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
然后它的行为正常:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
不过,也许可以在没有 tuple
中介的情况下做一些更简单的事情?
我想你可以使用 lambda(并将其保存在 std::function
中)来存储值。
类似于(注意:代码未经测试)(感谢 Oliv 的更正)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
如果你不想使用元组作为成员来保存值,你可以这样做:
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 ) );
}
现在 Builder::as_tie()
可以传递给 std::apply
(或向后移植的版本)或 make_from_tuple
。
自然地,operator T
技巧可用于 return 类型推导。但我通常会反对它。
您可以使用 Idx<n>
标签从第 n
个 Builder
:
中获取 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;
};
我有一个可变数据结构,每个 "layer" 包含一个字段。
如何使用存储在结构中的所有字段作为函数或构造函数的参数?
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;
};
总的来说,我希望能够做这样的事情:
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)
我找到的解决方案之一是传递包含字段的中介 tuple<...>
,然后使用 "unpacking" a tuple to call a matching function pointer 中描述的机制将其解压缩:
// unpacking helpers
template<int ...> struct seq {};
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> {};
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
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() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
然后它的行为正常:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
不过,也许可以在没有 tuple
中介的情况下做一些更简单的事情?
我想你可以使用 lambda(并将其保存在 std::function
中)来存储值。
类似于(注意:代码未经测试)(感谢 Oliv 的更正)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
如果你不想使用元组作为成员来保存值,你可以这样做:
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 ) );
}
现在 Builder::as_tie()
可以传递给 std::apply
(或向后移植的版本)或 make_from_tuple
。
自然地,operator T
技巧可用于 return 类型推导。但我通常会反对它。
您可以使用 Idx<n>
标签从第 n
个 Builder
:
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;
};