简体   繁体   中英

Template class accepting std::vector<T> or std::array<T>

I'm trying to write a templated getter function, that excepts std::array<T> and also std::vector<T> with arbitrary content of type T and returning one value of it

Map2d.h

#include <vector>
#include <array>

class Map2d {
    private:
    unsigned int m_width;
    unsigned int m_height;
    unsigned int m_size;

    public:
    Map2d(unsigned int width, unsigned int height)
        : m_width(width), m_height(height) {
        m_size = m_width * m_height;
    }

    template <typename T>
    struct is_array_or_vector {
        enum { value = false };
    };

    template <typename T, typename A>
    struct is_array_or_vector<std::vector<T, A>> {
        enum { value = true };
    };

    template <typename T, std::size_t N>
    struct is_array_or_vector<std::array<T, N>> {
        enum { value = true };
    };

    template <typename V, template <typename, typename...> class T, typename... Args>
    typename std::enable_if<is_array_or_vector<T<V, Args...>>::value, V>::type
    get(const T<V, Args...>& con, const unsigned int x, const unsigned int y) {
        assert(con.size() <= m_size);
        return con[m_width * y + x];
    }
};

Main.cpp

#include "Map2d.h"
int main() {
    Map2d map(10, 10);

    std::vector<int> v(100);
    std::cout << map.get(v, 5, 5) << std::endl; // works

    std::array<int, 100> a;
    std::cout << map.get(a, 5, 5) << std::endl; // not working

    std::list<int> l(100);
    std::cout << map.get(l, 5, 5) << std::endl; // should not work
    return 1;
}

What do I need to change to get this working? My version is compareable to this answer , with the difference, that the return value is void and not flexible.

I am thankful for every hint! :)

template <typename T>
typename std::enable_if<is_array_or_vector<T>::value, typename T::value_type>::type
get(const T& con, const unsigned int x, const unsigned int y) {
    assert(con.size() <= m_size);
    return con[m_width * y + x];
}

If, instead is_array_of_vector , you develop a custom type traits that define (or not) the contained type, as follows

template <typename>
struct enableAV
 { };

template <typename T, typename ... Ts>
struct enableAV<std::vector<T, Ts...>>
 { using type = T; };

template <typename T, std::size_t N>
struct enableAV<std::array<T, N>>
 { using type = T; };

you can avoid std::enable_if and write get() simply as follows

template <typename C>
typename enableAV<C>::type
   get(C const & con, const unsigned int x, const unsigned int y)
 {
   assert(con.size() <= m_size);
   return con[m_width * y + x];
 }
namespace notstd {
  namespace details {
    template<template<class...>class, class, class...>
    struct can_apply:std::false_type{};
    template<class...>struct voider{using type=void;};
    template<class...Ts>using void_t=typename voider<Ts...>::type;

    template<template<class...>class Z, class...Ts>
    struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
  }
  template<template<class...>class Z, class...Ts>
  using can_apply = details::can_apply<Z,void,Ts...>;
}
template<class T>
using dot_size_r = decltype( std::declval<T>().size() );
template<class T, class I=std::size_t>
using index_r = decltype( std::declval<T>()[ std::declval<I>() ] );

template<class T>
using can_dot_size = notstd::can_apply< dot_size_r, T >;
template<class T, class I=std::size_t>
using can_index = notstd::can_apply< index_r, T, I >;

can_dot_size and can_index tell you if a type T can be .size() d and [] indexed into.

template <typename C>
typename std::enable_if<
  can_dot_size<C const&>{} && can_index<C const&, unsigned int>{},
  typename std::decay<index_r<C const&, unsigned int>>::type
>::type
get(C const & con, const unsigned int x, const unsigned int y)
{
  assert(con.size() <= m_size);
  return con[m_width * y + x];
}

this get works on anything that has a .size() method and an [] index that accepts unsigned int.

This includes std::array , std::vector , std::deque , std::string or some other type that pretends to be array-like.

We could require .data()[] if we wanted to restrict to contiguous containers, which may be your intention, instead of raw [] .

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