将C++类转换为STL风格

5
我正在尝试编写一个类,其中包含几个std::vector作为数据成员,并提供了一部分vector的接口来访问它们:
class Mesh
{
public:
private:
  std::vector<Vector3> positions;
  std::vector<Vector3> normals;
  // Several other members along the same lines
};

您可以通过添加位置、法线和其他内容来操作网格。为了实现类似于STL的方式访问网格(从数组、其他容器等中添加),我考虑添加以下方法:

public:
  template<class InIter>
  void AddNormals(InIter first, InIter last);

问题在于,根据我的模板理解,这些方法必须在头文件中定义(似乎很合理;没有具体的迭代器类型,编译器就不知道如何为这个方法生成对象代码)。

  1. 这真的是个问题吗? 我的直觉是不要把大块的代码塞进头文件中,但我的C++有点生疏,除了玩具示例之外,没有太多STL经验,我不确定什么样的C++编程惯例可以接受。

  2. 是否有更好的方法在保留类似STL的通用编程风格的同时公开此功能?一种方法是:

(列表结束)

class RestrictedVector<T>
{
public:
  RestrictedVector(std::vector<T> wrapped)
    : wrapped(wrapped) {}

  template <class InIter>
  void Add(InIter first, InIter last)
  {
    std::copy(first, last, std::back_insert_iterator(wrapped));
  }

private:
  std::vector<T> wrapped;
};

然后在Mesh上公开这些实例,但这开始有点过度设计:P 任何建议都将不胜感激!

3个回答

5
这些方法必须在头文件中定义。 它们必须在头文件中定义,以便在实例化模板函数的翻译单元中使用时可用。如果您担心头文件中有太多模板会减慢使用Mesh但实际上不使用该模板函数的翻译单元的编译速度,则可以将实现移动到单独的头文件中。这对客户来说可能会稍微复杂一些,需要决定是否包含“全功能”类头文件,但实际上并不困难。
或者,对于这个特定的示例,您可以为Mesh定义一个输出迭代器,用于附加法线。然后,具有任意迭代器的客户端可以执行以下操作:
std::copy(first, last, mymesh.normalAdder());

他们在模板代码中所需要的唯一头文件是<algorithm>,他们很可能已经有了。
要自己实现,normalAdder()返回的对象需要重载operator++()operator*(),它本身需要返回一个代理对象(通常是*this),该对象实现operator=(const &Vector3)。这将附加到法线向量。但所有这些都是非模板代码,可以在您的.cpp文件中实现。
同样,在此示例中,normalAdder()可以只返回std::back_inserter(this.normals);,这是从<iterator>导入的模板。
至于您是否需要担心它——我认为当编译时间飙升时,更频繁的原因是不必要的依赖项,而不是由于头文件中的小块模板代码。一些大型项目似乎需要激烈的措施,但就个人而言,我从未使用过超过100个文件左右的项目。

两种解决方案的候选者,都使用了似乎非常特定于C ++的想法:) 非常感谢,这非常有帮助。 - anton.burger

2
我会建议你勇敢地创造一个干净易读的API/header。
Steve提供的返回输出迭代器的想法很巧妙,但对于你的类的客户端来说可能不太直观。当有人想要添加一些normals时,他们会想“哪里是添加normals的方法”,而不是“如何得到正常的输出迭代器”。(除非您在一个非常支持STL的工作室中。)
通过将模板化方法的实现定义移出类声明,可以在一定程度上缓解必须在头文件中定义实现的要求。
class Mesh
{
    public:
        void    AddPosition     ( Vector3 const & position );
        void    AddNormal       ( Vector3 const & normal );

        template< typename InIter >
        void    AddPositions    ( InIter const begin, InIter const end );

        template< typename InIter >
        void    AddNormals      ( InIter const begin, InIter const end );

    private:
        std::vector< Vector3 > positions;
        std::vector< Vector3 > normals;
};

template< typename InIter >
void
Mesh::AddPositions< InIter >( InIter const begin, InIter const end )
{
    positions.insert( positions.end(), begin, end );
}

template< typename InIter >
void
Mesh::AddNormals< InIter >( InIter const begin, InIter const end )
{
    normals.insert( normals.end(), begin, end );
}

0
这真的是一个问题吗?我的直觉是不要在头文件中放置大块的代码,但我的C++有点生疏,除了玩具示例外,没有太多STL经验,我不确定什么是“可接受”的C++编码实践。
我会问一个问题,你是否需要如此通用性?请记住,STL被编写为非常通用,以满足每个人的需求。您的代码是为您和您的团队编写的,以解决非常特定的问题。非通用的、针对特定问题的接口将很好地工作,并且对您的团队/问题领域的人更清晰。
否则……如果您需要那种通用性水平,那么您指定的内容非常好。您允许任何迭代器类型作为参数接受。从表面上看,您所拥有的一切都没有问题。这可能非常有用。

谢谢回复!这是否需要像我所追求的那样通用还有争议,但我进行这个项目的主要动力之一是为了恢复和现代化我的C++技能,这些技能自大学以来遗憾地被忽视了! - anton.burger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接