Использование полей, хранящихся в переменной структуре данных, в качестве аргументов метода - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть переменная структура данных, каждый «слой» содержит одно поле.

Как можно использовать все поля, хранящиеся в структуре, в качестве аргументов функции или конструктора?

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)

Ответы [ 5 ]

0 голосов
/ 14 ноября 2018

Вы можете использовать тег Idx<n> для получения tail из n -го Builder:

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;
};
0 голосов
/ 14 ноября 2018
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 трюк может использоваться для вычета типа возврата.Но я обычно советую против этого.

0 голосов
/ 14 ноября 2018

Полагаю, вы можете использовать лямбду (и сохранить ее в std::function) для хранения значений.

Что-то как (осторожно: код не проверен) (спасибо Оливу за исправление)

template <typename ... Ts>
class Builder
 {
   private:
      std::function<Result()>  fn;

   public:
      Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
       { }

      Result build ()
       { return fn(); }
 };
0 голосов
/ 14 ноября 2018

Если вы не хотите использовать кортеж в качестве члена для хранения значений, вы можете сделать это следующим образом:

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;
};
0 голосов
/ 14 ноября 2018

Одно из найденных мной решений - передать посреднику 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 посредника?

...