Использование enable_if в методе шаблона класса, чтобы проверить, являются ли два аргумента функции аргументами std :: array или оба std :: vector - PullRequest
2 голосов
/ 01 апреля 2020

Я не профессиональный программист, и я прошу прощения, если мой вопрос кажется наивным или неправильным.

Я пытаюсь, чтобы функция члена класса принимала std::array или std::vector в аргументах. В частности, я передаю два аргумента, которые должны быть либо std::array, либо оба std::vector.

ДО того, как пытаться заставить вышеописанное работать, минимальная настройка того, что у меня было, работала только с std::vector аргументами. минимальная настройка выглядит следующим образом, где v и p - это те, которые я хотел бы передать как векторы или массивы:

// original version

class my_class_A{
// stuf
};

class my_class_B{
// other stuf
};

class I_am_a_class_of_methods{
public:
  inline void my_method(const std::vector<my_class_A> &v,
                        const std::vector<std::array<uint64_t,2> > &p,
                        const my_class_B &x,
                        my_class_B &y){
    // `v` and `p` are used to modify `x` and store the result in `y`
    return;
  }; 
};

Я понимаю, что мог делать то, что я хочу с перегрузкой функций, однако я решил, что хочу достичь желаемого результата с помощью templates и std::enable_if, чтобы заставить меня узнать о них немного больше. Ну, я действительно многому научился, я не знал ... но этого недостаточно. Я в основном пытался настроить черту is_both_array_or_vector, и std::enable_if проверит, соответствует ли вызов шаблона шаблону. Последнее, что я попробовал, это следующее, которое скомпилировалось, но оно будет работать только для std::vector:

// current version

class my_class_A{
// stuf
};

class my_class_B{
// other stuf
};

// set up a type trait to check if both are vector or both are array
template <typename T1, typename T2>                                                                                                                                                                         
struct is_both_array_or_vector{                                                                                                                                                                             
  enum { value = false };                                                                                                                                                                                   
};                                                                                                                                                                                                          

template <typename T1, typename T2, typename A1, typename A2 >                                                                                                                                              
struct is_both_array_or_vector<std::vector<T1, A1>, std::vector<T2, A2> > {                                                                                                                                 
  enum { value = true };                                                                                                                                                                                    
};                                                                                                                                                                                                          

template <typename T1, typename T2, size_t D>                                                                                                                                                               
struct is_both_array_or_vector<std::array<T1, D>, std::array<T2, D> > {                                                                                                                                     
  enum { value = true };                                                                                                                                                                                    
}; 

// conditionally compile with enable_if
class I_am_a_class_of_methods{
public:
  template<template<typename,typename> U, template<typename,typename> S,
           typename Au, typename As> 
  typename std::enable_if<is_both_array_or_vector<U<my_class_A, Au>, S<std::array<uint64_t,2>,As> >::value >::type
  my_method(const U<my_class_A, Au> &v,
            const S<std::array<uint64_t,2>, As> &p,
            const my_class_B &x,
            my_class_B &y){
    // `v` and `p` are used to modify `x` and store the result in `y`
    return;
  }; 
};

Когда я компилирую с вызовом std::vector из main, все работает нормально. Это (конечно) не компилируется с вызовом std::array (компилятор, конечно, жалуется).

Если бы я только мог сделать аргументы шаблона As и Au, которые можно было бы интерпретировать как size_t тогда шаблон найдет совпадение с вызовом std::array. Однако это невозможно, так как у меня может быть либо typename, либо size_t, но не оба, насколько я знаю. Таким образом, мой вопрос: как мне заставить enable_if работать в этих условиях?

Ответы [ 2 ]

3 голосов
/ 01 апреля 2020

Обновление: как сказал Хавьер, вы можете сохранить шаблон частной функции, который будет обрабатывать общий случай, и перегрузить два метода для типов, которые вы хотите, и передать их этому шаблону.

class I_am_a_class_of_methods {
private:
  template <typename C1, typename C2>
  void my_method_priv(const C1& v, const C2& p, const my_class_B& x, my_class_B& y) {
    // ...
  }

 public:
  void my_method(const std::vector<my_class_A>& v,
                 const std::vector<array<uint64_t, 2>>& p,
                 const my_class_B& x,
                 my_class_B& y)
  { my_method_priv(v, p, x, y); }

  template <size_t N1, size_t N2>
  void my_method(const std::array<my_class_A, N1>& v,
                 const std::array<std::array<uint64_t, 2>, N2>& p,
                 const my_class_B& x,
                 my_class_B& y)
  { my_method_priv(v, p, x, y); }
};
1 голос
/ 01 апреля 2020

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

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

class I_am_a_class_of_methods
{
    template <typename FirstT, typename SecondT>
    struct Enable { };
    template <typename A1, typename A2>
    struct Enable<std::vector<my_class_A, A1>,
                  std::vector<std::array<uint64_t,2>, A2>>
    {
        using type = void;
    };
    template <std::size_t N1, std::size_t N2>
    struct Enable<std::array<my_class_A, N1>,
                  std::array<std::array<uint64_t,2>, N2>>
    {
        using type = void;
    };

public:
    template <typename T1, typename T2>
    typename Enable<std::decay_t<T1>, std::decay_t<T2>>::type
    my_method(T1 const &v, T2 const &p, my_class_B const &x, my_class_B &y)
    {
        // whatever
    }
};
...