【问题标题】:Simple example with templates带有模板的简单示例
【发布时间】:2012-06-12 07:08:37
【问题描述】:

这是一个简单的问题,我确信以前已经回答过,但我似乎找不到好的答案。

我有课,点:

template<class T>
Point{
\\code
}

...现在我想要一个点向量,其中一些将 T 作为整数,而将 T 作为双精度数。我想写类似的东西

template<class T>
std::vector<Point<T> > points;

但是,唉,这不会编译错误“在'模板'之前的预期主表达式”。我无法对这段代码坐立不安以使其工作。同样相关的是点在主类中,所以我不能将模板声明粘贴在函数之外。

如果有人可以指导我找到解决方案,我将不胜感激。

谢谢。

【问题讨论】:

  • 一个向量只能包含一种类型的元素。所以你可以选择 Point 或 Point 但不能混合使用。
  • 不应该是std::vector&lt;Point&lt;int&gt; &gt; points吗?
  • 你想要一个模板化的typedef 吗?
  • 类模板的不同实例化是完全不相关的类型:Point&lt;int&gt;Point&lt;double&gt;,从编译器的角度来看,是两种不同的类型,没有任何共同之处。如果您想以相同的方式操作各种类型的点,则需要使用通用基类。
  • @LucTouraille:使用继承会强制 OP 使用引用或指针,它可能不是“值类型”类的好选择(我假设 Point 是)。在这种情况下,变体方法可能会更好。

标签: c++ templates


【解决方案1】:

如果您的目标是拥有一个同时包含Point&lt;int&gt;Point&lt;double&gt;vector,您可以使用Boost Variant

typedef boost::variant<Point<int>, Point<double> > VariantPoint;

然后:

std::vector<VariantPoint> my_vector;

my_vector.push_back(Point<int>(1, 0));
my_vector.push_back(Point<double>(1.5f, 2.0f));

会工作。请注意,要在之后检查元素,您可能必须使用 visitor pattern 所记录的 here

如果您的目标是拥有只能包含Point 一种类型的不同向量类型,那么您可以使用:

template<typename T> using PointVector = std::vector<Point<T>>; // C++11

// Now you can write:
PointVector<int> my_vector;

// Which is equivalent to:
std::vector<Point<int>> my_vector;

或者,如果 C++11 不是一个选项:

template<typename T> struct PointVector
{
  typedef std::vector<Point<T> > Type;
}

然后:

PointVector<int>::Type my_vector;

【讨论】:

  • 感谢您的完整回答。据我了解,Boost 和 C++11 不在标准 C++ 库中,我正在编写将在不一定具有这些包的计算机上运行的代码。但是,我正在寻找您的第一个涉及变体的解决方案。有没有办法在没有 Boost 的情况下做到这一点? (比如说,正如另一个答案所建议的那样。)
  • @alexvas:如果您对深入研究 Boost Variant 代码感到满意,您可能可以轻松地重现它。或者您可以从 Boost 中提取文件(Boost 有一个工具可以干净地提取它的子集)。 Boost 可以在许多平台上运行,也许您可​​以使用自己的代码发布它(或者说服那些决定这样做的人?)
【解决方案2】:

要获得单一种类的向量,我会使用继承:

template <typename T>
struct PointVector : public std::vector< Point<T> >
{
};

注意,继承只是实现模板typedef的等效机制。这意味着,PointVector 不应包含任何数据成员或虚函数。但是,@ereOn 的建议是首选,并在this question 的答案中进行了讨论。

实现变体的老式方法是使用联合。

class IntOrDouble {
    union {
        int i;
        double d;
    };
    bool is_int;
    bool is_double;
public:
    IntOrDouble () : is_int(false), is_double(false) {}
    IntOrDouble (int x) : is_int(true), is_double(false) { i = x; }
    IntOrDouble (double x) : is_int(false), is_double(true) { d = x; }
    int operator = (int x) {
        is_int = true;
        is_double = false;
        return i = x;
    };
    double operator = (double x) {
        is_int = false;
        is_double = true;
        return d = x;
    };
    operator int () const {
        if (is_int) return i;
        if (is_double) return d;
        return 0;
    }
    operator double () const {
        if (is_double) return d;
        if (is_int) return i;
        return 0;
    }
};
typedef std::vector< Point<IntOrDouble> > PointVector;

但是对于这个用例来说,这一切似乎都有些过头了。我只会使用 double 的向量,除非内存真的很紧。

【讨论】:

  • 我不会投反对票,但在这种情况下继承是错误的,原因有几个。最重要的原因是std::vector 并非旨在公开继承。我不确定标准是怎么说的,但我非常怀疑它是否有一个虚拟析构函数,这可能会导致一些微妙的问题。如果有的话,它应该是私有继承,但我仍然不推荐它。
  • @ereOn:感谢您的意见。我已经使用 STL 容器的继承很长时间了,所以得知它可能不安全,我感到很惊讶。我会对您找到的任何支持这一点的文档感兴趣。问候。
  • @ereOn:我澄清了我的回答,表明继承仅用作执行模板类型定义的一种手段。
  • std::vector 继承不被标准禁止(显然),但它没有虚拟析构函数。也就是说,如果一个人通过指向基类的指针访问deletePointVector,它可能会导致内存泄漏。此外,在这种情况下,公共继承还允许 OP 写入导致 slicingstd::vector&lt;Point&lt;int&gt; &gt; vec = PointVector&lt;int&gt;();,您可能不希望这样。 union 解决方案要正确得多,但如果boost::variant 是一个选项,那就更好了。
  • @ereOn:谢谢,我得到了您对虚拟析构函数的关注。由于继承旨在实现模板 typedef 的等价物,因此子类中不会有任何数据成员,而且我也不介意切片,因为 typedef 应该具有等价性。问候。
猜你喜欢
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 2014-10-30
相关资源
最近更新 更多