【问题标题】:Define Return value as * or & based on custom Iterator<T>根据自定义 Iterator<T> 将返回值定义为 * 或 &
【发布时间】:2014-11-28 23:10:27
【问题描述】:

更新

我在 STL 不可用的环境中处理一些容器代码。我试图概括我的迭代器以减少代码重复。我遇到过这样一种情况,我有一个可以包含对象或指向对象的指针的容器。我在尝试编译以下(人为的)代码时遇到了编译问题:

struct Object
{
};

template<typename Container, typename T>
struct IteratorBase
{
    IteratorBase( Container * container, T curr ) : _container( container ), _curr( curr ) { }

    Container * _container;
    T           _curr;

    T &         GetValue() const { return _curr; }

    void        Invalidate() { _curr = NULL; }
};


template<typename T>
struct Container
{
    typedef typename T * Type;

    typedef IteratorBase<Container<T>, Type>            Iterator;

    Iterator        GetIterator() { return Iterator( this, storage ); }

    T               storage[10];
};


int main(int argc, char* argv[])
{
    Container<Object> containerByAddress;

    const Object* pA = containerByAddress.GetIterator().GetValue();

    return 0;
}

错误是

error C2440: 'return' : cannot convert from 'Object *const ' to 'Object *&'
while compiling class template member function 'Object &IteratorBase<Container,T>::GetValue(void) const'

所以我做了一些更改,通过使用以下方法推断 T 的类型来更改返回值的方式:

template<typename T>
struct DetermineReturnType
{
    typedef T &     ReturnType;
};

template<typename T>
struct DetermineReturnType<T*>
{
    typedef  T *    ReturnType;
};

template<typename Container, typename T>
struct IteratorBase
{
    IteratorBase( Container * container, T curr ) : _container( container ), _curr( curr ) { }

    Container * _container;
    T           _curr;

    typedef typename DetermineReturnType<T>::ReturnType ReturnType;

    ReturnType  GetValue() const { return _curr; }

    void        Invalidate() { _curr = NULL; }
};

现在我的迭代器使用 & 或 * 基于 T。

但我的问题是 - 这是一个合理的解决方案吗?有什么办法可以解决这个问题吗?

请注意,我不能使用 STL、Boost 或最新版本的 C++,因为这是一个已有几年历史的嵌入式系统。

感谢您的任何见解。

【问题讨论】:

  • Object * &amp; GetValue(); 如果您的指针以与值相同的方式存储,则应该没问题。为什么说是临时的,不能这样退?
  • 我认为没有理由在这里使用专业化。如果TObject*,那么T&amp; GetValue() 将已经是Object*&amp; GetValue()
  • 是的,你们都是对的。当我扩展我的示例时,一切正常,但是在实际代码的密集丛林中,这并没有被编译,而且它似乎与 *'s 和 &'s 一个 const 有关。可能我在错误的地方有 const 。我会做更多的挖掘和回信。
  • @Captain Obvlious - 所以 Object * a = iterator.GetValue() where T is Object* 没有问题,即使函数签名是 Object * & Iterator::GetValue() ?跨度>
  • 正确。您只是返回对容器中元素的引用并将引用的值复制到a。它是一个指针值这一事实无关紧要,它仍然是一个值。 FWIW 如果您正确实现了GetValue(),则指针不应该是右值(临时)。

标签: c++ templates reference iterator return-type


【解决方案1】:

问题是GetValue() 被声明为const,但您返回的是对成员变量的非const 引用。如果编译器允许您这样做,您将能够在迭代器的可能const 限定实例中修改成员变量。您可以通过提供const 和非const 版本的GetValue() 来轻松解决这个问题。编译器会根据迭代器实例的 CV 限定符选择合适的版本。

T const& GetValue() const { return _curr; }
T& GetValue() { return _curr; }

不幸的是,这不太可能让您获得您正在寻找的真实行为。您真的不应该在这里返回引用,因为它只允许您更改迭代器中的元素,而不是容器中的元素。要到达您想要的位置,您需要更改的不仅仅是 GetValue 实现,并重新设计您的迭代器以使用容器中的元素,而不是在本地维护它。

下面是一个基本示例,说明您可以如何处理此问题。它返回对容器中元素的引用,允许您通过迭代器修改容器。由于我没有您的 Container 的完整版本,因此我使用的是裸数组,但可见的最终结果应该很容易掌握。

#include <iostream>

struct Object { };

template<typename T>
struct IteratorBase
{
    IteratorBase(T* initialPtr) : _curr(initialPtr) {}
    T* _curr;

    T const & GetValue() const { return *_curr; }
    T& GetValue() { return *_curr; }
    void next() { ++_curr; }
    void prev() { --_curr; }
};

int main()
{
    Object a, b, c, d, e;
    Object  *objects[] = { &a, &b, &c, &d, &e };
    IteratorBase<Object*> it(objects);

    for (int i = 0; i < sizeof(objects) / sizeof(*objects); i++)
    {
        std::cout << it.GetValue() << "\n";
        it.GetValue() = NULL;
        it.next();
    }

    it = IteratorBase<Object*>(objects);
    for (int i = 0; i < sizeof(objects) / sizeof(*objects); i++)
    {
        std::cout << it.GetValue() << "\n";
        it.next();
    }
}

【讨论】:

  • @Caption Obvlious - 这确实似乎解决了问题,我可以理解为什么。只是迭代器、typedef 和容器的迭代器接口以及旧的单元测试框架使这变得困难重重。我很感激帮助。一旦我完成对二十多个容器的更改,我会将其标记为已回答。
  • 我猜我之前使用 & 或 * 选择器的解决方案起作用的原因是它返回了指针的副本,而不是对它的引用。您认为哪个是可取的或首选的?
  • 仔细查看您的实现后,您不应该返回引用,而是返回普通的旧指针。如果你想返回一个引用,你应该返回一个引用容器中元素的引用,而不是迭代器的成员。这样做的原因是,通过返回引用,您可以更改迭代器指向的内容,如果您引用容器中的元素,则改为更改容器的内容。
  • 好吧,给定一个 Container 和一个迭代器,调用 Container::Iterator::GetValue() 应该给我一个我可以操作的 Object*。所以是的,我应该返回一个指针。但是,在某些情况下,Container 类型是 Container - 不是指针,而是按值存储的。考虑一个 Vector 或 Set - 现在我希望我的迭代器的 GetValue 返回对该对象的引用,既可以避免按值复制,也可以允许更改对象。因此 DeduceReturnType - 这样 GetValue() 将通过引用返回值类型,并通过指针返回。
  • 为了清楚,我在一般意义上使用“容器”这个词。我有 Lists、Maps、Sets、HashTables,我正在尝试重用基本的迭代器接口以尽量减少代码重复。
猜你喜欢
  • 2011-06-16
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多