【问题标题】:Overloading -> operator with temporary object使用临时对象重载 -> 运算符
【发布时间】:2011-06-09 21:44:39
【问题描述】:

我正在为一个网格类编写一个包装器,试图实现一个更直观的界面。网格使用有些笨拙的迭代器,可以递增和比较但不能取消引用;相反,您必须从网格中获取它们关联的特征句柄。同样,特征句柄是愚蠢的,因此要从顶点获取位置/颜色/任何内容,您必须在网格上调用另一个函数:

Mesh mesh;
VertexIterator vertices = mesh.VerticesBegin();
VertexHandle vertex = mesh.VertexIteratorToHandle(vertices);
Vector3 vertexPosition = mesh.GetVertexPosition(vertex);

我希望能够这样做:

MeshWrapper<Mesh> wrapper(mesh);
Vector3 vertexPosition = wrapper.VerticesBegin()->Position();

为了使这种更方便的样式成为可能,我为网格、它的迭代器和句柄提供了包装类:

template <class Mesh>
class SmartVertexHandle
{
public:
    SmartVertexHandle(Mesh::VertexHandle dumbVertexHandle, Mesh* parent);

    Vector3 Position();
    Vector3 Color(); 
    // etc ...
private:  
    Mesh* m_parent;

    typename Mesh::VertexHandle m_dumbVertexHandle;
}

template <class Mesh>
class SmartVertexIterator
{
public:
    SmartVertexHandle<Mesh>* operator->();
    // etc ...
private:
    Mesh* m_parent;
    typename Mesh::VertexIterator m_dumbVertexIterator;
}

-> 操作符的实现是困扰我的。我需要返回一个指向 SmartVertexHandle 的指针,但我能从网格中得到的只是一个愚蠢的 Mesh::VertexHandle。我目前这样处理这个问题:

template <class Mesh>
class SmartVertexIterator
{
public:
    SmartVertexHandle<Mesh>* operator->()
    {
        m_vertexWrapper = SmartVertexHandle<Mesh>(m_parent->VertexIteratorToHandle(m_dumbVertexIterator), m_parent);
        return &m_vertexWrapper;
    }
private:
    Mesh* m_parent;
    typename Mesh::VertexIterator m_dumbVertexIterator;

    SmartVertexHandle<Mesh> m_vertexWrapper;
}

这让我觉得非常可怕且充满危险,更不用说浪费空间了。有什么办法可以避免吗?

抱歉问了这么长的问题,谢谢:)

【问题讨论】:

  • 如果您知道要包装哪个类(即Mesh),为什么MeshWrapper 采用模板参数?
  • 我有几个不同的网格类具有相同的 API(例如运行时/离线版本)。模板与我遇到的问题无关,对吧?
  • 你的情况下的迭代器是什么?它支持哪些功能?它只是取消引用(产生SmartVertexHandle)并增加吗?
  • 是的,据我所知,这无关紧要。
  • 愚蠢的迭代器(Mesh::VertexIterator等)只做前缀++--。智能迭代器(SmartVertexIterator 等)还应该执行后缀递增/递减(不是问题)并取消对 SmartVertexHandle 的引用。

标签: c++ templates operator-overloading


【解决方案1】:

自定义operator-&gt; 的特殊之处在于它的行为就像在返回值上递归调用operator-&gt;。所以给定T some_type::operator-&gt;() const,这表现为:

some_type()->some_member;
// moral equivalent:
some_type()::operator->()->some_member;

通常不会注意到,因为在您尝试时返回了一个普通指针,因此在第一个 operator-&gt; 之后使用内置 -&gt;,因此链只有 1 深。但是,您似乎可以根据需要使用此行为:

SmartVertexHandle<Mesh> SmartVertexIterator<Mesh>::operator->();

SmartVertexHandle<Mesh>* SmartVertexHandle<Mesh>::operator->()
{ return this; }

然后当用户执行wrapper.VerticesBegin()-&gt;Position()时,VerticesBegin()返回一个SmartVertexIterator,第一个operator-&gt;返回一个SmartVertexHandle,第二个隐式operator-&gt;返回一个指向这个临时句柄的指针,其中内置的-&gt; 调用SmartVertexHandle::Position。据推测,智能手柄的构造和设计是为了知道如何做parent-&gt;GetVertexPosition(parent-&gt;GetVertexHandle( ... ) )。然后,当评估完整的表达式时,临时的 SmartVertexHandle 会优雅地消失。

请注意,我在 SmartVertexHandleSmartVertexIterator 中重复使用了你的名字,但我无法知道你的类是否可以(重新)设计成这样使用。在理想的世界中,我不一定必须为用户设计一个单独的SmartVertexHandle;我可能会写一个SmartVertexIterator::proxy 类型让operator-&gt; 返回,然后使用上述技术。

总而言之,我认为您当前存储句柄的方法是急切版本,其中每当构造迭代器时都会计算和存储句柄,并且可能会在迭代器为时重新计算,例如递增。我的方法是一个懒惰的版本,仅在被要求时才构建句柄(但即使迭代器相同,也不会存储它并每次都重建它)。我不认为第一个是“非常糟糕的”,因为我不知道与迭代器相比构建句柄有多昂贵,或者每个迭代器步骤取消引用/使用句柄的频率。第三种方法甚至可能更懒惰。

在这两种情况下,我都建议不要将SmartVertexHandle 暴露给用户(当然也许你有要求)。

【讨论】:

  • 非常感谢,这是一个很好的答案!在我的情况下,我认为需要单独的SmartVertexHandle,因为我还在其上模板化了SmartVertexIterator(为简洁起见,从问题代码中省略了该部分)。虽然智能迭代器对所有可能的特征负载都有效,但我需要几个不同的智能句柄来处理不同的网格类型。
猜你喜欢
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多