C ++ приведен векторный тип на месте - PullRequest
3 голосов
/ 31 января 2012

Возможно ли это сделать без создания новой структуры данных? Предположим, у нас есть

struct Span{
    int from;
    int to;
}
vector<Span> s;

Мы хотим получить целочисленный вектор из s напрямую, приведя

vector<Span> s;

до

vector<int> s;

чтобы мы могли удалить / изменить некоторые элементы "from", "to", а затем привести их обратно к

vector<Span> s;  

Ответы [ 3 ]

2 голосов
/ 31 января 2012

Это не очень хорошая идея, но я покажу вам, как.

Вы можете получить необработанный указатель на целое число следующим образом:

int * myPointer2 = (int*)&(s[0]);

но это действительно плохая практика, потому что вы не можете гарантировать, что структура span не имеет никакого отступа , поэтому, хотя она может хорошо работать для меня и для вас сегодня, мы не можем сказать много за другие системы.

#include <iostream>
#include <vector>


struct Span{
    int from;
    int to;
};


int main()
{

    std::vector<Span> s;

    Span a = { 1, 2};
    Span b = {2, 9};
    Span c = {10, 14};

    s.push_back(a);
    s.push_back(b);
    s.push_back(c);


    int * myPointer = (int*)&(s[0]);

    for(int k = 0; k < 6; k++)
    {
        std::cout << myPointer[k] << std::endl;
    }

    return 0;
}

Как я уже сказал, это сложное повторное толкование часто работает, но очень опасно и не имеет кроссплатформенных гарантий, которые вы обычно ожидаете от C / C ++.

Следующая худшая вещь - это то, что на самом деле будет делать то, что вы просили, но вы никогда не должны делать. Вот код, за который вы можете получить:

// Baaaad mojo here: turn a vector<span> into a vector<int>:
std::vector<int> * pis = (std::vector<int>*)&s;

for ( std::vector<int>::iterator It = pis->begin(); It != pis->end(); It++ )
        std::cout << *It << std::endl;

Обратите внимание, как я использую указатель на вектор и указываю на адрес векторного объекта s. Я надеюсь, что внутренности обоих векторов одинаковы, и я могу использовать их просто так. Для меня это работает, и хотя стандартные шаблоны, к счастью, могут требовать, чтобы это имело место, это не так, как правило, для шаблонных классов (см. Такие вещи, как заполнение и специализация шаблонов).

Попробуйте вместо этого скопировать массив (см. Ссылку 2 ниже) или просто использовать s 1 .from и s [2] .to.

Связанное чтение:

  1. Являются ли элементы std :: vector гарантированно смежными?
  2. Как преобразовать вектор в массив в C ++
1 голос
/ 31 января 2012

Если sizeof(Span) == sizeof(int) * 2 (то есть Span не имеет заполнения), тогда вы можете безопасно использовать reinterpret_cast<int*>(&v[0]), чтобы получить указатель на массив int, который вы можете перебирать. Вы можете гарантировать структуры без заполнения для каждого компилятора: __attribute__((__packed__)) в GCC и #pragma pack в Visual Studio.

Однако есть способ, который гарантируется стандартом. Определите Span так:

struct Span {
    int endpoints[2];
};

endpoints[0] и endpoints[1] требуются , чтобы быть смежными. Если хотите, добавьте несколько аксессуаров from() и to() для вашего удобства, но теперь вы можете использовать reinterpret_cast<int*>(&v[0]) для своего сердца.

Но если вы собираетесь много заниматься этим манипулированием указателями, вы можете захотеть создать свою собственную vector -подобную структуру данных, более подходящую для этой обработки - такую, которая предлагает больше гарантий безопасности, чтобы вы можно избежать выстрела ногами.

1 голос
/ 31 января 2012

Отказ от ответственности: я абсолютно не знаю о том, что вы пытаетесь сделать. Я просто делаю обоснованные догадки и показываю возможные решения, основанные на этом. Надеюсь, я угадаю одно правильно, и вам не придется делать сумасшедшие махинации с глупыми забросками.

Если вы хотите удалить определенный элемент из вектора, все, что вам нужно сделать, это найти его и удалить, используя функцию erase . Вам нужен итератор для вашего элемента, и получение этого итератора зависит от того, что вы знаете об этом элементе. Дано std::vector<Span> v;:

  • Если вы знаете его индекс:

    v.erase(v.begin() + idx);
    
  • Если у вас есть объект, равный тому, который вы ищете:

    Span doppelganger;
    v.erase(std::find(v.begin(), v.end(), doppelganger));
    
  • Если у вас есть объект, который соответствует тому, что вы ищете, но хотите удалить все равных элементов, вам нужна идиома erase-remove:

    Span doppelganger;
    v.erase(std::remove(v.begin(), v.end(), doppelganger)),
            v.end());
    
  • Если у вас есть какой-то критерий для выбора элемента:

    v.erase(std::find(v.begin(), v.end(),
                      [](Span const& s) { return s.from == 0; }));
    
    // in C++03 you need a separate function for the criterion
    bool starts_from_zero(Span const& s) { return s.from == 0; }
    
    v.erase(std::find(v.begin(), v.end(), starts_from_zero));
    
  • Если у вас есть какой-то критерий и вы хотите удалить все элементов, которые соответствуют этому критерию, вам снова понадобится идиома erase-remove:

    v.erase(std::remove_if(v.begin(), v.end(), starts_from_zero)),
            v.end());
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...