简体   繁体   中英

Using enable_if, in class template method, to check if two function arguments arguments are both std::array or both std::vector

I am not a professional coder and I apologise if my question seams naive or malformed.

I am trying to have a class member function take std::array or std::vector in the arguments. Specifically I am passing two arguments that should either both be std::array or both be std::vector .

BEFORE trying to make the above work, a minimum set up of what I had was working only with std::vector arguments. a minimum set up is as follows, where v and p are the ones I desire to have passed as vectors or arrays:

// 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;
  }; 
};

I understand I could do what I want with function overloading, however, I decided I wanted to achieve the desired result with templates and std::enable_if , to force me to learn a bit more about them. Well I did learn a lot I didn't know... but not enough it seams. I have basically tried setting up a trait is_both_array_or_vector , and std::enable_if will check to see if the template call matches the trait. The final thing I tried is the following, which did compile, but it will only work for 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;
  }; 
};

When I compile with std::vector call from main everything works fine. This (of course) doesn't compile with std::array call (compiler of course complains).

If only I could make template arguments As and Au be able to be interpreted as size_t , then the template would find a match with an std::array call. This doesn't seam possible however, since I can have either typename or size_t , not both as far as I know. Thus my question is, how would I get enable_if to work in this circumstance?

Update: As Javier said, you can keep a private function template that will handle the general case, and overload two methods for the types you want and pass them to that template.

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); }
};

I would highly recommend using function overloads as the optimal solution here. However, you said you wanted to experiment and learn. Since that is the case, please allow me to demonstrate one approach in that vein.

Note that this is a bit complicated because you want explicit template arguments, but there is not that much more metaprogramming involved in the detection, and it at least gives you an idea of how to approach something like this when it is wise to do so - but in this case just use function overloads.

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
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM