Одно из найденных мной решений - передать посреднику tuple<...>
, содержащему поля, а затем распаковать его, используя механизм, описанный в «распаковка» кортежа для вызова соответствующего указателя функции :
// 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
посредника?