【问题标题】:Fixed number of parameter pack elements to initialize templated class固定数量的参数包元素来初始化模板类
【发布时间】:2019-08-25 20:37:52
【问题描述】:

一般的想法是有一个通用类来表示n维空间中的一个点(“类Point”)。只要它们的类型相同,它就应该采用任意数量的参数。 我使用可变参数模板参数包做到了这一点,它似乎工作正常。

现在我想要一个简单的别名,比如“Point2i”,它一次只接受整数参数(简单,只需指定类型 T)和两个参数(提示二维空间)(这是我失败的地方到目前为止)。

/**
 * @brief Point in n-dimensional space.
 */
template <typename T = int> class Point {
public:
    /**
     * @brief Default constructor for an empty polygon.
     * Points have to be added manually via @ref add_point.
     */
    template <typename... Ts>
    Point(Ts... coords) {
        m_coordinates = { std::forward<Ts>(coords)... };
    }

    /**
     * @brief Dimensions of the point coordinate space.
     * @return Number of dimensions.
     */
    size_t dims() const {
        return m_coordinates.size();
    }

    /**
     * @brief Array subscript operator.
     * @param dim The dimension you want.
     * @return The coordinate in the specified dimension.
     */
    T& operator[] (const size_t &dim) {
        return m_coordinates[dim];
    }

private:
    /// Coordinates of the point in n-dimensional space, where n = vector size.
    std::vector<T> m_coordinates;
};

template <typename... Ts, typename = typename std::enable_if<sizeof...(Ts) == 2>::type>
using Point2i = Point<int>(Ts...);

问题出在最后两行:“Point2i”的东西不起作用。我从 GCC 9 得到的错误是:“point.h:52:23: error: template parameter pack must be the last template parameter”。

第 52 行是带有“模板”的行

知道如何按照我想要的方式进行这项工作吗?我想对于有 C++ 模板元编程经验的人来说这很容易。

【问题讨论】:

  • 您不能为对构造函数的特定调用创建类型别名
  • 一般情况下,您不应该将类与动态分配的数组一起用于二维点。这将对性能造成巨大影响。您应该通过T val[N];std::array&lt;T,N&gt; val; 编写一个带有静态分配数据的单独类。
  • 另一个问题,对Point 类使用模板参数包构造函数不是一个好主意。你最好通过std::initializer_list&lt;T&gt;写一个简单的非模板构造函数。

标签: c++ templates variadic-templates


【解决方案1】:

为什么不根据您要使用的 n 维来模板类?然后,当元素数量与尺寸不匹配时,您可以消除重载:

template<class T, int N=-1> // -1 means dimensions are not specified
class Point {
public:
  template<class... Ts, std::enable_if_t<(sizeof...(Ts), N) == -1>* = nullptr>
  Point(Ts... coords) : m_coordinates{ coords... }
  { }

  template<class... Ts, std::enable_if_t<(N != -1 && sizeof...(Ts) == N)>* = nullptr>
  Point(Ts... coords) : m_coordinates{ coords... }
  { }
  // ...
};
using Point2i = Point<int, 2>;

【讨论】:

    【解决方案2】:

    我认为 using 不可能用于类的构造函数。

    我能想象到的最好的就是经典的make_something() 函数。

    我的意思是……有点像

    template <typename... Ts,
              typename = typename std::enable_if<sizeof...(Ts) == 2>::type>
    Point<int> Point2i (Ts... ts)
     { return { ts... }; }
    

    Off Topic Unrequested Suggestion 1:尽可能使用构造函数初始化列表;避免(如果可能)在构造函数体内进行初始化

    我的意思是......而不是

    template <typename... Ts>
    Point(Ts... coords) {
        m_coordinates = { std::forward<Ts>(coords)... };
    }
    

    更好

    template <typename... Ts>
    Point (Ts ... coords) : m_coordinates { std::forward<Ts>(coords)... }
     { }
    

    Off Topic Unrequested Suggestion 2:如果要使用完美转发,请使用转发引用。

    所以构造函数变成了

    template <typename... Ts>
    Point (Ts && ... coords) : m_coordinates { std::forward<Ts>(coords)... }
     { } //   ^^ <-- add forwarding references
    

    也在point2i函数中

    template <typename... Ts,
              typename = typename std::enable_if<sizeof...(Ts) == 2>::type>
    Point<int> Point2i (Ts && ... ts) 
     { return { std::forward<Ts>(ts)... }; }
    

    Off Topic Unrequested Suggestion 3:我想你的 Point 对象是固定大小的。在这种情况下,我建议考虑将点的大小添加为模板参数的假设。这样您就可以使用std::array 而不是std::vector

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-20
      • 2016-03-25
      相关资源
      最近更新 更多