Объединение двух std::list разных типов: возможно?

У меня есть std::list типа Foo* и другой тип Bar* разного размера. Оба типа реализуют систему позиционирования, которая позволяет сортировать список по z-координате для порядка отрисовки (на самом деле просто точка со значениями x, y, z, при этом они сортируются с помощью функции меньше, чем Predicate, по значению z).

Помимо вышеперечисленного, они совершенно разные. Есть ли способ объединить списки, чтобы я мог сравнивать ВСЕ значения z друг с другом, а не только их собственные типы?

Прямо сейчас, например, либо все Foos отсортированы, либо все Bars отсортированы; затем рисуются либо все Foos, либо все Bars. Это приводит к тому, что даже если у Bar z меньше, чем у Foo, он будет нарисован сверху. Явно не ожидаемые последствия.

Пока я печатал это, у меня было прозрение, будет ли работать параллельная обработка? Отсортируйте каждый список отдельно, но затем поочередно рисуйте их, Foo, Bar, Foo, Bar и т. д., или это приведет к той же проблеме? Некоторые рисунки выше других независимо от значения z?

Спасибо.


person Casey    schedule 05.07.2011    source источник
comment
Я думаю, что ваше прозрение сработает хорошо... держите отдельный итератор для каждого списка и всегда рисуйте, а затем итерируйте список, чей «текущий элемент» имеет более низкое значение Z. Повторяйте, пока не дойдете до конца обоих списков. И вуаля.   -  person Jeremy Friesner    schedule 05.07.2011


Ответы (1)


Вы можете попробовать наследовать оба типа от базы, которая включает позицию, возможно, с virtual Draw():

struct Base
{
    Point pos;

    virtual ~Base() {}
    virtual void Draw() = 0;
};

struct Foo : base {};
struct Bar : base {};

std::list<Base*> list;

//...

list.sort([](Base *left, Base *right)
{
    return left->pos.z < right->pos.z;
});

for(auto iter = list.begin(), end = list.end(); iter != end; ++iter)
{
    (*iter)->Draw();
}

Если вы хотите, чтобы списки были отдельными, чередование рисунков Foo и Bar не сработает, если два из Foo предшествовали одному Bar.

Но вы думаете в правильном направлении. Вы можете сортировать по отдельности, а затем объединять два списка во время рисования:

foo_list.sort();
bar_list.sort();

auto fiter = foo_list.begin(), fend = foo_list.end();
auto biter = bar_list.begin(), bend = bar_list.end();

while(fiter != fend && biter != bend)
{
    // draw whichever Foo or Bar is closest, and increment only that iterator.

    if((*fiter)->z_pos < (*biter)->z_pos)
    {
        (*fiter)->Draw();
        ++fiter;
    }
    else
    {
        (*biter)->Draw();
        ++biter;
    }
}

// reached the end of one of the lists. flush out whatever's left of the other.

for(; fiter != fend; ++fiter)
{
    (*fiter)->draw();
}

for(; biter != bend; ++biter)
{
    (*biter)->draw();
}

Вы также можете использовать вариант, если хотите сохранить только один список, но иметь два совершенно разных типа:

struct visitor
{
    float operator()(Foo* f) const { return f->z_position; }
    float operator()(Bar* b) const { return b->z_position; }
};

std::list<boost::variant<Foo*, Bar*>> list;

//...

list.sort([](boost::variant<Foo*, Bar*> const &left, boost::variant<Foo*, Bar*> const &right)
{
    return apply_visitor(visitor(), left) < apply_visitor(visitor(), right);
});

for(auto iter = list.begin(), end = list.end(); iter != end; ++iter)
{
    (*iter)->Draw();
}
person Cory Nelson    schedule 05.07.2011
comment
До сих пор не было возможности протестировать его, индивидуальная сортировка и объединение во время рисования работали отлично! :D Спасибо за понимание. - person Casey; 07.07.2011