Множественные паттерны varadic шаблоны в C ++ - PullRequest
0 голосов
/ 13 мая 2018

Я не думаю, что это возможно на основании того, что я прочитал, однако я надеюсь, что кто-то здесь может знать о каком-то решении, которое заставило бы это работать.

У меня есть векторный (математический) класс для C ++

template <typename T, size_t N> class vec;

И хотите создать неординарную friend функцию, применимую для применения функции к этим векторам поэлементно

т.е.

template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&... args);

, который действителен (еще не проверен)

однако я хочу добиться такой картины, как

template <typename F> friend vec<typename std::result_of<F&&(T&&)>::type, N> apply(F&& f, const vec<T, N>& V);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<T, N>& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const T& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const T& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<U, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const U& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const vec<U, N>& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const U& V1, const vec<T, N>& V2);

обратите внимание, что только один из аргументов должен быть вектором, любые скаляры будут транслироваться по длине вектора.

Идея состоит в том, что apply(pow, /*vec<float,N>*/V, /*int*/n) -> {pow(V.v[i],n)...}, где i -> 0 ... N, а не apply(pow, /*vec<float,N>*/V, /*int*/n) -> apply(pow, /*vec<float,N>*/V, /*vec<int,N>*/tmp{/*int*/n}) {pow(V.v[i],tmp.v[i])...}

Так что я хотел бы иметь возможность написать что-то вроде следующего (что не является допустимым C ++, но должно дать представление о том, чего я хочу достичь)

template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&||scalar<Args>::type... args) {
    vec<typename std::result_of<pow(Args&&...)>::type, N> r;
    for (int i= 0; i < N; i++) { r = f((is_vec<Args>?args.v[i]:args)...); }
    return r;
}

EDIT:

Основываясь на комментариях Фрэнка, я ищу что-то вроде

template<typename F, typename ...Args, size_t N>
vec<typename std::enable_if<sum<is_vec<Args,N>...>::value > 0, std::result_of<F&&(base_type<Args>::type&&...)>::type>::type, N>
(F&& f, Args&&...args) {
    vec<typename std::result_of<F&&(base_type<Args>::type&&...)>::type, N> result;
    for(std::size_t i = 0 ; i < N ; ++i) { result.v[i] = f(extract_v(std::forward<Args>(args),i)...); }
    return result;
}

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

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Не уверен, что понимаю, чего именно ты хочешь, но ...

Мне кажется, что могут быть полезны черты нестандартного типа для извлечения из списка типов измерения Vec, если (если и только если) в списке типов есть хотя бы один Vec и нет Vec разной длины.

Я предлагаю что-то следующее, в значительной степени основанное на специализации шаблонов,

template <std::size_t, typename ...>
struct dimVec;

// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;

// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
 { };

// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;

// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
 { };

с помощью шаблонной статической переменной

template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };

Теперь должно быть легко.

Вы можете написать функцию apply(), которая получает список переменных аргументов типов Args... и поддерживает SFINAE, если dimVecV<Args...> определено

template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
 { return applyH1(std::make_index_sequence<N>{}, f, as...); }

Обратите внимание, что переменная N используется для включения / выключения функции SFINAE, но сама по себе полезна: она используется для передачи std::index_sequence из 0 в N-1 в первую вспомогательную функцию applyH1()

template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
 { return { applyH2<Is>(f, as...)... }; }

, которые инициализируют возвращенное значение Vec единичными значениями, вычисленными из второй вспомогательной функции applyH2()

template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
 { return f(extrV<I>(as)...); }

, который использует набор шаблонных функций extrV()

template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
 { return v[I]; }

template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
 { return v; }

для извлечения I -ого элемента из Vec или для передачи скалярного значения.

Это немного долго, но не особо сложно.

Ниже приведен полный рабочий пример

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t N>
class Vec;

template <std::size_t, typename ...>
struct dimVec;

// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;

// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
 { };

// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;

// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
 { };

template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };

template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
 { return v[I]; }

template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
 { return v; }

template <typename T, std::size_t N>
class Vec
 {
   private:
      std::array<T, N> d;

   public:
      template <typename ... Ts>
      Vec (Ts ... ts) : d{{ ts... }}
       { }

      T & operator[] (int i)
       { return d[i]; }

      T const & operator[] (int i) const
       { return d[i]; }
 };


template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
 { return f(extrV<I>(as)...); }

template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
 { return { applyH2<Is>(f, as...)... }; }

template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
 { return applyH1(std::make_index_sequence<N>{}, f, as...); }

long foo (int a, int b)
 { return a + b + 42; }

int main ()
 {
   Vec<int, 3U>  v3;
   Vec<int, 2U>  v2;

   auto r1 { apply(foo, v2, v2) };
   auto r2 { apply(foo, v3, v3) };
   auto r3 { apply(foo, v3, 0)  };

   static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
   static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
   static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );

   // apply(foo, v2, v3); // compilation error
   // apply(foo, 1, 2);   // compilation error

 }
0 голосов
/ 13 мая 2018

Вы можете достичь желаемого за счет сочетания частичной специализации шаблона и расширения пакета параметров.

#include <array>

template<typename T, std::size_t N>
using Vec = std::array<T, N>;

template<typename T>
struct extract {
  static auto exec(T const& v, std::size_t) {return v;}
  enum { size = 1 };
};

template<typename T, std::size_t N>
struct extract<Vec<T,N>> {
  static auto exec(Vec<T,N> const& v, std::size_t i) {return v[i];}
  enum {size = N};
};

template<typename T>
auto extract_v(T const& v, std::size_t i) {return extract<T>::exec(v, i);}

template<typename... args>
struct extract_size {
    enum {size = 1};
};

template<typename first, typename... rest>
struct extract_size<first, rest...> {
    enum {
        rest_size_ = extract_size<rest...>::size,
        self_size_ = extract<first>::size,
        size = rest_size_ > self_size_ ? rest_size_ : self_size_ 
    };

    static_assert(self_size_ == 1 || rest_size_ == 1 || rest_size_ == self_size_, "");
};

template<typename F, typename... args_t>
auto apply(F const& cb, args_t&&... args) {
  constexpr std::size_t size = extract_size<std::decay_t<args_t>...>::size;
  using result_t = decltype(cb(extract_v(std::forward<args_t>(args),0)...));

  Vec<result_t, size> result;

  for(std::size_t i = 0 ; i < size ; ++i) {
    result[i] = cb(extract_v(std::forward<args_t>(args),i)...);
  }

  return result;
}
...