如何在不公开所使用的容器的情况下公开迭代器?

发布于 2024-07-06 15:27:42 字数 567 浏览 6 评论 0原文

我已经使用 C# 一段时间了,回到 C++ 很头疼。 我正在尝试将我的一些实践从 C# 转移到 C++,但我发现了一些阻力,我很乐意接受您的帮助。

我想为这样的类公开一个迭代器:

template <class T>
class MyContainer
{
public:
    // Here is the problem:
    // typedef for MyIterator without exposing std::vector publicly?

    MyIterator Begin() { return mHiddenContainerImpl.begin(); }
    MyIterator End() { return mHiddenContainerImpl.end(); }

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

我正在尝试一些不是问题的事情吗? 我应该输入 def std::vector< 吗? T >::迭代器? 我希望只依赖于迭代器,而不是实现容器......

I have been using C# for a while now, and going back to C++ is a headache. I am trying to get some of my practices from C# with me to C++, but I am finding some resistance and I would be glad to accept your help.

I would like to expose an iterator for a class like this:

template <class T>
class MyContainer
{
public:
    // Here is the problem:
    // typedef for MyIterator without exposing std::vector publicly?

    MyIterator Begin() { return mHiddenContainerImpl.begin(); }
    MyIterator End() { return mHiddenContainerImpl.end(); }

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

Am I trying at something that isn't a problem? Should I just typedef std::vector< T >::iterator? I am hoping on just depending on the iterator, not the implementing container...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

小耗子 2024-07-13 15:27:42

您可能会发现以下文章很有趣,因为它恰好解决了您发布的问题:关于对象之间的张力 - C++ 中的面向通用编程以及类型擦除可以做什么

You may find the following article interesting as it addresses exactly the problem you have posted: On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

七颜 2024-07-13 15:27:42

我之前已经完成了以下操作,以便获得一个独立于容器的迭代器。 这可能有点过头了,因为我也可以使用一个 API,其中调用者传入一个向量&,该向量应该填充所有元素,然后调用者可以从直接向量。

template <class T>
class IterImpl
{
public:
    virtual T* next() = 0;
};

template <class T>
class Iter
{
public:
    Iter( IterImpl<T>* pImpl ):mpImpl(pImpl) {};
    Iter( Iter<T>& rIter ):mpImpl(pImpl) 
    {
        rIter.mpImpl = 0; // take ownership
    }
    ~Iter() {
        delete mpImpl; // does nothing if it is 0
    }
    T* next() {
    return mpImpl->next(); 
    }
private:
    IterImpl<T>* mpImpl; 
};

template <class C, class T>
class IterImplStl : public IterImpl<T>
{
public:
    IterImplStl( C& rC )
    :mrC( rC ),
    curr( rC.begin() )
    {}
    virtual T* next()
    {
    if ( curr == mrC.end() ) return 0;
    typename T* pResult = &*curr;
    ++curr;
    return pResult;
    }
private:
    C& mrC;
    typename C::iterator curr;
};


class Widget;

// in the base clase we do not need to include widget
class TestBase
{
public:
    virtual Iter<Widget> getIter() = 0;
};


#include <vector>

class Widget
{
public:
    int px;
    int py;
};

class Test : public TestBase
{
public:
    typedef std::vector<Widget> WidgetVec;

    virtual Iter<Widget> getIter() {
        return Iter<Widget>( new IterImplStl<WidgetVec, Widget>( mVec ) ); 
        }

    void add( int px, int py )
    {
        mVec.push_back( Widget() );
        mVec.back().px = px;
        mVec.back().py = py;
    }
private:
    WidgetVec mVec;
};


void testFn()
{
    Test t;
    t.add( 3, 4 );
    t.add( 2, 5 );

    TestBase* tB = &t;
    Iter<Widget> iter = tB->getIter();
    Widget* pW;
    while ( pW = iter.next() )
    {
        std::cout << "px: " << pW->px << " py: " << pW->py << std::endl;
    }
}

I have done the following before so that I got an iterator that was independent of the container. This may have been overkill since I could also have used an API where the caller passes in a vector<T*>& that should be populated with all the elements and then the caller can just iterate from the vector directly.

template <class T>
class IterImpl
{
public:
    virtual T* next() = 0;
};

template <class T>
class Iter
{
public:
    Iter( IterImpl<T>* pImpl ):mpImpl(pImpl) {};
    Iter( Iter<T>& rIter ):mpImpl(pImpl) 
    {
        rIter.mpImpl = 0; // take ownership
    }
    ~Iter() {
        delete mpImpl; // does nothing if it is 0
    }
    T* next() {
    return mpImpl->next(); 
    }
private:
    IterImpl<T>* mpImpl; 
};

template <class C, class T>
class IterImplStl : public IterImpl<T>
{
public:
    IterImplStl( C& rC )
    :mrC( rC ),
    curr( rC.begin() )
    {}
    virtual T* next()
    {
    if ( curr == mrC.end() ) return 0;
    typename T* pResult = &*curr;
    ++curr;
    return pResult;
    }
private:
    C& mrC;
    typename C::iterator curr;
};


class Widget;

// in the base clase we do not need to include widget
class TestBase
{
public:
    virtual Iter<Widget> getIter() = 0;
};


#include <vector>

class Widget
{
public:
    int px;
    int py;
};

class Test : public TestBase
{
public:
    typedef std::vector<Widget> WidgetVec;

    virtual Iter<Widget> getIter() {
        return Iter<Widget>( new IterImplStl<WidgetVec, Widget>( mVec ) ); 
        }

    void add( int px, int py )
    {
        mVec.push_back( Widget() );
        mVec.back().px = px;
        mVec.back().py = py;
    }
private:
    WidgetVec mVec;
};


void testFn()
{
    Test t;
    t.add( 3, 4 );
    t.add( 2, 5 );

    TestBase* tB = &t;
    Iter<Widget> iter = tB->getIter();
    Widget* pW;
    while ( pW = iter.next() )
    {
        std::cout << "px: " << pW->px << " py: " << pW->py << std::endl;
    }
}
舂唻埖巳落 2024-07-13 15:27:42

这应该可以满足您的要求:

typedef typename std::vector<T>::iterator MyIterator;

来自 Accelerated C++

只要您有一个依赖于模板参数的类型(例如 vector),并且您想要使用该类型的成员(例如 size_type) >,它本身就是一种类型,您必须在整个名称前添加 typename 以使实现知道将该名称视为类型。

This should do what you want:

typedef typename std::vector<T>::iterator MyIterator;

From Accelerated C++:

Whenever you have a type, such as vector<T>, that depends on a template parameter, and you want to use a member of that type, such as size_type, that is itself a type, you must precede the entire name by typename to let the implementation know to treat the name as a type.

三生池水覆流年 2024-07-13 15:27:42

我不确定你所说的“不公开 std::vector”是什么意思,但实际上,你可以这样定义你的 typedef:

typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references

稍后你将能够更改这些 typedef,而用户不会注意到任何事情......

顺便说一句,如果您希望您的类充当容器,那么公开一些其他类型被认为是很好的做法:

typedef typename std::vector<T>::size_type size_type;
typedef typename std::vector<T>::difference_type difference_type;
typedef typename std::vector<T>::pointer pointer;
typedef typename std::vector<T>::reference reference;

如果您的类需要:

 typedef typename std::vector<T>::const_pointer const_pointer;
 typedef typename std::vector<T>::const_reference const_reference;

您将在这里找到所有这些 typedef 的含义: 关于向量的 STL 文档

编辑:添加了评论中建议的 typename

I am unsure about what you mean by "not exposing std::vector publicly" but indeed, you can just define your typedef like that:

typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references

You will be able to change these typedefs later without the user noticing anything ...

By the way, it is considered good practice to also expose a few other types if you want your class to behave as a container:

typedef typename std::vector<T>::size_type size_type;
typedef typename std::vector<T>::difference_type difference_type;
typedef typename std::vector<T>::pointer pointer;
typedef typename std::vector<T>::reference reference;

And if needed by your class:

 typedef typename std::vector<T>::const_pointer const_pointer;
 typedef typename std::vector<T>::const_reference const_reference;

You'll find the meaning of all these typedef's here: STL documentation on vectors

Edit: Added the typename as suggested in the comments

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文