【问题标题】:Template class accepting std::vector<T> or std::array<T>接受 std::vector<T> 或 std::array<T> 的模板类
【发布时间】:2017-06-23 11:26:59
【问题描述】:

我正在尝试编写一个模板化的 getter 函数,它除了 std::array&lt;T&gt;std::vector&lt;T&gt; 之外的任意内容类型为 T 并返回它的一个值

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

我需要进行哪些更改才能使其正常工作?我的版本可以与answer 相比,不同之处在于返回值是无效的并且不灵活。

感谢您的每一个提示! :)

【问题讨论】:

  • 你应该从::std::integral_constant&lt; bool, false &gt;而不是enum { value = false };派生is_array_or_vector
  • 你能指出我更多正确的方向吗?不幸的是,我对你的小费无能为力:((
  • @JensMetzner:你考虑过使用the GSL type span吗?毕竟,您为什么要禁止获取任何代表Ts 连续范围的对象?

标签: c++ c++11 templates sfinae c++17


【解决方案1】:
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];
}

【讨论】:

  • 对不起,但是...typename... Args 不是多余的吗?
  • @max66 这是一个复制/粘贴错误,比superflous还要糟糕。
【解决方案2】:

如果,而不是is_array_of_vector,您开发了一个自定义类型特征来定义(或不​​定义)包含的类型,如下所示

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

你可以避开std::enable_if,写成get(),简单如下

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

【讨论】:

  • 这提高了易读性!只是一个问题:那我需要std::enable_if 做什么?我想,这就是std::vector存在的原因。
  • @JensMetzner - std::enable_it 是泛型类型特征;通常你不需要开发类似enableAVis_array_or_vector 的东西;但是,如果必须,您可以开发一些更量身定制的东西:您需要包含的类型(或什么都不需要),因此更简单的是开发一种类型特征来提供您需要的类型,而不是提供一个布尔值的类型,该布尔值激活 std::enable_if 来给出你是你需要的类型。无论如何,我不明白你的意思“[std::enable_if] 是std::vector 存在的原因。std::vector 是 c++ 的旧容器;std::enable_if 是 c++11。
  • 我的错!我的意思是std::enable_if(“我想,这就是std::enable_if 存在的原因。”)。谢谢你的回答,这让我明白了!
【解决方案3】:
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_sizecan_index 告诉你 T 类型是否可以被 .size()d 和 [] 索引到。

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

这个get 适用于任何具有.size() 方法和接受无符号整数的[] 索引的东西。

这包括std::arraystd::vectorstd::dequestd::string 或其他一些伪装成类似数组的类型。

如果我们想限制为连续容器,我们可能需要.data()[],这可能是您的意图,而不是原始的[]

【讨论】:

  • type &amp;&amp; type 如何在 enable_if 条件下产生值?
  • @JohannesSchaub-litb 我应该使用模板变量。现已修复。
猜你喜欢
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 2016-11-07
相关资源
最近更新 更多