Перегрузка -> оператор с временным объектом - PullRequest
1 голос
/ 10 июня 2011

Я пишу оболочку для класса меша, пытаясь реализовать более интуитивный интерфейс.Сетка использует несколько глупых итераторов, которые можно увеличивать и сравнивать, но не разыменовывать;вместо этого вы должны получить их дескриптор связанного объекта из сетки.Точно так же дескрипторы объектов глупы, поэтому, чтобы получить позицию / цвет / что-либо из вершины, вы должны вызвать другую функцию на сетке:

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

Это кажется мне довольно ужасным и чреватым опасностью, не говоря уже о том, что я трачу пространство.Есть ли способ избежать этого?

Извините за длинный вопрос и спасибо:)

1 Ответ

2 голосов
/ 10 июня 2011

У пользовательского operator-> есть особенность, что он ведет себя так, как будто operator-> рекурсивно вызывается для возвращаемого значения. Таким образом, учитывая T some_type::operator->() const, это ведет себя как:

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

Обычно этого не замечают, потому что при попытке вернуть простой указатель, так что после первого operator-> используется встроенный -> и, следовательно, цепочка имеет глубину только 1. Похоже, что вы можете использовать это поведение для своих нужд, используя:

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

и

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

Затем, когда пользователь делает wrapper.VerticesBegin()->Position(), VerticesBegin() возвращает SmartVertexIterator, первый operator-> возвращает SmartVertexHandle, а второй, неявный, operator-> возвращает указатель на этот временный дескриптор, где встроенный -> звонки SmartVertexHandle::Position. Предположительно умная ручка сконструирована и предназначена для того, чтобы знать, как делать parent->GetVertexPosition(parent->GetVertexHandle( ... ) ). Затем, когда вычисляется полное выражение, временное SmartVertexHandle изящно исчезает.

Обратите внимание, что я повторно использовал ваши имена для SmartVertexHandle & SmartVertexIterator, но у меня нет возможности узнать, могут ли ваши классы быть (пере) спроектированы для такого использования. В идеальном мире я бы не обязательно разработал бы отдельную SmartVertexHandle для пользователя; Я бы, вероятно, написал бы тип SmartVertexIterator::proxy для возврата operator->, а затем использовал бы описанную выше технику.

В целом, я думаю, что ваш текущий подход к сохранению дескриптора - это нетерпеливая версия, где дескриптор вычисляется и сохраняется всякий раз, когда создается итератор, и, предположительно, пересчитывается всякий раз, когда итератор, например ,. увеличивается. Мой подход - более ленивая версия, создающая дескриптор только по запросу (но не сохраняющая его и перестраивающая его каждый раз, даже когда итератор идентичен). Я не думаю, что первый «довольно ужасен», так как я не знаю, как дорого создавать дескрипторы по сравнению с итераторами, или как часто дескрипторы разыменовываются / используются для каждого шага итератора. Третий подход может быть даже ленивее.

В обоих случаях я бы посоветовал не показывать пользователю SmartVertexHandle (конечно, может быть, у вас есть требования).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...