用于操作基础/派生对象容器的函数

发布于 2024-07-18 04:50:03 字数 1070 浏览 7 评论 0 原文

考虑以下带有数组的算法:

class MyType;
{
    // some stuff
}

class MySubType:MyType
{
    // some stuff
}

void foo(MyType** arr, int len)
{
    for (int i = 0;i<len;i++)
        // do something on arr[i]->
}

void bar()
{
    MySubType* arr[10];
    // initialize all MySubType*'s in arr
    foo(&arr, 10);
}

这里没有什么太奇特的。 我的问题是 - 如何使用模板来做到这一点?

void foo(std::vector<MyType>& s)
{
    std::vector<MyType>::iterator i;
    for (i = s.begin(); i != s.end(); i++)
        // do stuff on *i
}

所以,在 bar 中,我不能这样做:

void bar()
{
    std::vector<MySubType> s;
    foo(s);  // compiler error
}

错误:类型 std::vector 的引用初始化无效 >& 来自 std::vector 类型的表达式 >

有什么方法可以做这样的事情吗?

基本上,如果有办法做到这一点:

std::vector<MySubType> s;
std::vector<MyType>& t = s;

我会很高兴......

consider the following algorithm with arrays:

class MyType;
{
    // some stuff
}

class MySubType:MyType
{
    // some stuff
}

void foo(MyType** arr, int len)
{
    for (int i = 0;i<len;i++)
        // do something on arr[i]->
}

void bar()
{
    MySubType* arr[10];
    // initialize all MySubType*'s in arr
    foo(&arr, 10);
}

Nothing too fancy here. My question is - how do I do this with templates?

void foo(std::vector<MyType>& s)
{
    std::vector<MyType>::iterator i;
    for (i = s.begin(); i != s.end(); i++)
        // do stuff on *i
}

so, in bar, I can't do this:

void bar()
{
    std::vector<MySubType> s;
    foo(s);  // compiler error
}

error: invalid initialization of reference of type std::vector<MyType, std::allocator<MyType> >& from expression of type std::vector<MySubType, std::allocator<MySubType> >

Is there some way to do something like this?

Basically, if there's a way to do this:

std::vector<MySubType> s;
std::vector<MyType>& t = s;

I'd be happy...

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

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

发布评论

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

评论(9

孤独岁月 2024-07-25 04:50:03

这可能会解决您的问题

template <typename T>
void foo(std::vector<T>& s)
{
    typename std::vector<T>::iterator i;
    for (i = s.begin(); i != s.end(); i++)
        // do stuff on *i
}

This might fix your problem

template <typename T>
void foo(std::vector<T>& s)
{
    typename std::vector<T>::iterator i;
    for (i = s.begin(); i != s.end(); i++)
        // do stuff on *i
}
一抹淡然 2024-07-25 04:50:03

扩展 kuoson 的答案,即惯用的 C++ 风格是将迭代器传递给函数而不是容器。

template<typename Iterator>
void foo(const Iterator & begin, const Iterator & end)
{
    Iterator i;
    for (i = begin;  i != end;  ++i)
        // do stuff on *i
}

To expand on kuoson's answer, the idiomatic C++ style is to pass iterators to a function rather than containers.

template<typename Iterator>
void foo(const Iterator & begin, const Iterator & end)
{
    Iterator i;
    for (i = begin;  i != end;  ++i)
        // do stuff on *i
}
任性一次 2024-07-25 04:50:03

这就是问题所在 - 如果 s 和 t 指向同一个对象,那么什么会阻止您将 MyOtherSubType (与 MySubType 无关)放入 t 中? 这将使 s 包含不是 MySubType 的对象。 我不知道有任何类型安全的编程语言可以让你做到这一点。 如果允许的话,想象一下我们会遇到的问题:

//MySubType inherits from MyType
//MyOtherSubType also inherits from MyType

std::vector<MySubType> s;
std::vector<MyType>& t = s;

MyOtherSubType o;
t.push_back(o);

由于 t 和 s 是不同名称下的完全相同的对象,因此 s[0] 处的元素不是 MySubType。 巨大的问题 - 我们永远无法确定我们的向量是否包含它们应该包含的类型! 因此,编译不允许它。

Here's the problem with this - if s and t point to the same object, what's to stop you putting MyOtherSubType (not related to MySubType) into into t? That would make s contain objects that are not MySubType. I don't know of any type-safe programming language that lets you do this. If it were allowed, imagine the problems we'd have:

//MySubType inherits from MyType
//MyOtherSubType also inherits from MyType

std::vector<MySubType> s;
std::vector<MyType>& t = s;

MyOtherSubType o;
t.push_back(o);

Since t and s are exactly the same object under different names, the element at s[0] is something that is not MySubType. Huge problem - we could never be sure that our vectors contained the types they are supposed to! Thus, the compiled disallows it.

铃予 2024-07-25 04:50:03

既然你说,“这里没什么太花哨的”,我不确定你是否意识到这一点:

你的原始代码已损坏; 或者如果你幸运的话,它现在可能不会被破坏,但它非常脆弱,一旦有人做了比查看 MySubType 类更多的事情,它就会被破坏。

问题是您将 MyType* 传递给 foo(),但它实际上指向 MySubType 数组。 如果 MySubType 对象恰好比 MyType 对象大(如果您向派生类添加了任何内容,则很可能如此),则在foo() 函数将不正确。

这是派生类对象数组的严重且经典的陷阱之一。

Since you say, "Nothing too fancy here", I'm not sure you realize this:

Your original code is broken; or if you're lucky it might not be broken right now, but it's quite fragile and will break as soon as someone does something more than look at the MySubType class.

The problem is that you're passing a MyType* to foo() but it really points to an array of MySubType. If a MySubType object happens to be larger than a MyType object (which is pretty likely if you've added anything to the derived class), then the pointer arithmetic done in the foo() function will be incorrect.

This is one of the serious and classic pitfalls of arrays of derived class objects.

柒夜笙歌凉 2024-07-25 04:50:03

如果您想在单个向量中存储多个不同 MyType 派生类型的对象(我怀疑您这样做,尽管对于这个特定示例来说这不是必需的),您将需要使用 std::vector 而不是 std::vector。 此建议类似于 Michael Burr 针对您的原始指针代码提出的建议。

这确实有一个不幸的副作用,您无法将 std::vector 隐式转换为 std::vector 来调用foo() 与。 但转换代码并不太繁重:

void foo(std::vector<MyType*>& s)
{
    ...
}

void bar()
{
    std::vector<MySubType*> s;

    // Populate s
    ...

    std::vector<MyType*> u(s.begin(), s.end());    // Convert
    foo(u);
}

或者,只需让 bar() 从一开始就使用 std::vector 即可。

If you want to store objects of multiple different MyType-derived types inside a single vector (as I suspect you do, although that's not necessary for this particular example), you'll need to use a std::vector<MyType*> instead of std::vector<MyType>. This suggestion is analogous to that proposed by Michael Burr for your original pointer code.

This does have the unfortunate side-effect that you cannot implicitly convert a std::vector<MySubType*> to a std::vector<MyType*> to call foo() with. But the conversion code is not too onerous:

void foo(std::vector<MyType*>& s)
{
    ...
}

void bar()
{
    std::vector<MySubType*> s;

    // Populate s
    ...

    std::vector<MyType*> u(s.begin(), s.end());    // Convert
    foo(u);
}

Or, just have bar() use a std::vector<MyType*> from the beginning.

清旖 2024-07-25 04:50:03

C++ 不支持模板类型的协变,这与 C# 目前不支持的程度相同,原因大致相同。 如果您想这样做,最好使您的向量在公共接口类型(指向该类型的指针)上模板化,并使函数 foo 接受该类型的向量。

C++ does not support covariance on template types, any more than C# currently does, much for the same reasons. If you want to do this, it is best to make your vector be templatised on a common interface type (of pointers to that type), and make the function foo accept a vector of that type.

浮生未歇 2024-07-25 04:50:03

不幸的是,没有。 不同的模板专业化被视为不同的类; 您无法在 std::vectorstd::vector 之间进行转换。

您可以将 foo() 的“do stuff”公共位拆分为一个单独的函数,并对每个向量进行单独的循环(或者可能使用 std::foreach)。

Unfortunately, there isn't. Different template specialisations are considered different classes; you can't convert between std::vector<MyType> and std::vector<MySubType>.

You could split the "do stuff" common bit of foo() into a separate function and have a separate loop over each vector (or possibly use std::foreach).

若无相欠,怎会相见 2024-07-25 04:50:03

使用 boost (用于智能指针):

foo( std::vector<boost::shared_ptr<MyType> >& v )
{
   std::for_each( v.begin(),
                  v.end(),
                  do_something );
}

bar()
{
    std::vector<boost::shared_ptr<MyType> > s;
    // s.push_back( boost::shared_ptr<MyType> ( new MySubType() ) );
    foo( s ); 
}

Using boost (for smart pointer):

foo( std::vector<boost::shared_ptr<MyType> >& v )
{
   std::for_each( v.begin(),
                  v.end(),
                  do_something );
}

bar()
{
    std::vector<boost::shared_ptr<MyType> > s;
    // s.push_back( boost::shared_ptr<MyType> ( new MySubType() ) );
    foo( s ); 
}
你曾走过我的故事 2024-07-25 04:50:03

如果我知道这些 foobars 背后的真正代码是什么,那么也许我会知道得更好,但是 STL 中是否已经有解决您问题的方法了?

for_each(s.begin(), s.end(), DoStuffOnI());

只需将“do stuff on *i”代码放入函数或函子中即可:

struct DoStuffOnI : public std::unary_function<MyType&,void> {
    void operator()(MyType& obj) {
        // do stuff on *i
    }
};

如果您担心发送两个参数而不是一个参数,那么好吧,也许您可​​以执行以下操作:

template<typename In>
struct input_sequence_range : public std::pair<In,In> {
    input_sequence_range(In first, In last) : std::pair<In,In>(first, last)
    {
    }
};

template<typename C>
input_sequence_range<typename C::iterator> iseq(C& c)
{
    return input_sequence_range<typename C::iterator>(c.begin(), c.end());
}

template<typename In, typename Pred>
void for_each(input_sequence_range<In> r, Pred p) {
    std::for_each(r.first, r.second, p);
}

然后像这样调用 for_each :

for_each(iseq(s), DoStuffOnI());

If I'd know what's the real code behind those foobars then maybe I'd know better, but isn't there already a solution for your problem in the STL?

for_each(s.begin(), s.end(), DoStuffOnI());

Just put your "do stuff on *i" code in a function or a functor:

struct DoStuffOnI : public std::unary_function<MyType&,void> {
    void operator()(MyType& obj) {
        // do stuff on *i
    }
};

If you're bothered about sending away two parameters instead of one, then ok, maybe you can do something like:

template<typename In>
struct input_sequence_range : public std::pair<In,In> {
    input_sequence_range(In first, In last) : std::pair<In,In>(first, last)
    {
    }
};

template<typename C>
input_sequence_range<typename C::iterator> iseq(C& c)
{
    return input_sequence_range<typename C::iterator>(c.begin(), c.end());
}

template<typename In, typename Pred>
void for_each(input_sequence_range<In> r, Pred p) {
    std::for_each(r.first, r.second, p);
}

Then call for_each like so:

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