указатели на класс в динамически выделенном массиве boost multi_array, без компиляции

Я довольно новичок в С++ с Boost.

Я хочу, чтобы объект класса «мир» имел массив с именем «кусок» типа «октринода». Раньше у меня был обычный одномерный массив, и это работало нормально. Теперь я пытаюсь перейти к использованию трехмерного массива с функциональностью multi_array Boost, и я действительно не уверен, что делаю неправильно.

Упрощенный код:

class world {
public:

  typedef boost::multi_array<octreenode, 3> planetchunkarray;  // a boost_multi for chunks
  typedef planetchunkarray::index index;
  planetchunkarray *chunk;

  world(double x,double y,double z,
        int widtheast, int widthnorth, int height) :
        originx(x), originy(y), originz(z),
        chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height) {

    chunk = new planetchunkarray(boost::extents[chunksnorth][chunkseast][chunksup]);
    planetchunkarray::extent_gen extents;

    for (int cz = 0; cz < chunksnorth; ++cz) {
      for (int cx = 0; cx < chunkseast; ++cx) {
        for (int cy = 0; cy < chunksup; ++cy) {
          (*chunk)[cz][cx][cy] = new octreenode(1,72);
        }
      }
    }
  }
};

После чего, если я попытаюсь выполнить задание

корень->планета[0]->кусок[0][0][0]->материал = 4;

Я получаю сообщение об ошибке:

error: base operand of '->' has non-pointer type 'boost::detail::multi_array::sub_array<octreenode, 1u>'|

"octreenode" имеет соответствующий конструктор, и эта строка работала с идентичным синтаксисом, когда была просто:

корень->планета[0]->кусок[0]->материал = 4;

(с одномерным массивом). Точно так же, хотя он отлично скомпилирован с одномерным массивом, пытаясь передать фрагмент функциям, которые ожидают указатель на объект «octreenode», например:

compactoctree(root->planet[p]->chunk[cz][cx][cy], 0, 14);

генерирует ошибку

error: cannot convert 'boost::detail::multi_array::sub_array<octreenode, 1u>' to 'octreenode*' for argument '1' to 'short int compactoctree(octreenode*, int, int)'|

Был бы очень признателен за любые предложения, я уверен, что упускаю что-то очевидное.


person Riot    schedule 25.09.2012    source источник
comment
(предложение о разыменовании чанка поступило от groups.google. com/forum/?fromgroups=#!topic/boost-list/ )   -  person Riot    schedule 25.09.2012


Ответы (1)


Ваш массив имеет тип значения (octreenode), а не тип указателя (octreenode*)

Поэтому вы не должны пытаться назначать указатель на динамически выделяемый октринод (по умолчанию new предназначен для выделения кучи).

Вместо этого просто присвойте значение:

      (*chunk)[cz][cx][cy] = octreenode(1,72);

На самом деле, нет причин использовать new в мультимассиве в первую очередь:

ОБНОВИТЬ

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

Итак, вот: если вы действительно хотите инициализировать все элементы массива одним и тем же значением,

  1. Вы можете сделать циклы более эффективными, на мгновение забыв о формах массива:

    std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
    

    Если вы знаете, что octreenode относится к типу POD, вы можете написать

    std::uninitialzed_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
    

    но умная реализация библиотеки в любом случае вызовет fill_n (потому что нет никакого выигрыша). Вы можете использовать uninitialized_fill_n, если octreenode не является типом POD, но его можно легко разрушить.

  2. На самом деле, в первую очередь нет причин использовать new в мультимассиве. Вы можете просто использовать список инициализации конструктора для создания члена multi_array


Жить на Coliru

#include <boost/multi_array.hpp>
#include <type_traits>

struct octreenode { int a; int b; };

class world {
public:
    world(double x, double y, double z, int widtheast, int widthnorth, int height)
            : 
                originx(x), originy(y), originz(z), 
                chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height),
                chunk(boost::extents[chunksnorth][chunkseast][chunksup])
    {
        octreenode v = { 1, 72 };
        std::fill_n(chunk.data(), chunk.num_elements(), v);
    }

private:
    double originx, originy, originz;
    int chunkseast, chunksnorth, chunksup;

    typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
    typedef planetchunkarray::index index;
    planetchunkarray chunk;
};

int main() {
    world w(1,2,3,4,5,6);
}
person sehe    schedule 09.01.2015
comment
Поскольку я спрашивал об этом несколько лет назад, немного сложно вспомнить контекст, но массив multi объявляется в куче, потому что он слишком велик для стека; желание, чтобы куски были параллельны в памяти, было намерением выделить множественный массив не указателей в куче, а не массив указателей, который мог бы быть повсюду и перегружать кеш при разыменовании и итерации. Тем не менее, оглядываясь назад, я вижу, что моя попытка создать новый октринод таким образом была неуместной, и я должен был использовать форму размещения нового. Я опубликую ответ. - person Riot; 12.01.2015
comment
надеюсь нет! Ваш ответ побудил меня вернуться к этому, и, надеюсь, это обсуждение будет полезно кому-то, кто придет сюда из Google в будущем. - person Riot; 12.01.2015
comment
@Riot Кашель. Это действительно учит меня этому. В исходном сообщении не было ничего, что сделало бы ваш собственный ответ более применимым. Кроме того, я только что объяснил в вашем ответе, почему это плохо для вашего здоровья. Итак, скажите мне еще раз, как я должен радоваться тому факту, что я (значительно) потратил время на старый ответ, в результате чего кто-то, кто придет сюда из Google в будущем, может получить посредственные ответы, потому что существуют два противоречивых ответа и правильный не было ни одного голоса? - person sehe; 13.01.2015
comment
Если указанная вам неприменимость вашего ответа к исходной проблеме вызывает такой уровень страданий, то, возможно, вы правы, не желая тратить на это так много времени; конечно, комментарии, которые вы сделали по моему принятому (и, предположительно, отклоненному вами) ответу, могли быть полезным вкладом в собственный ответ; но это не тот ответ, который вы на самом деле опубликовали, не так ли? Вы можете заявить, что использование размещения new вредно для вашего здоровья, но это полезная и стандартная функция языка, и это один из примеров, когда его использование вполне допустимо. Мне жаль, что ты так расстроен. - person Riot; 15.01.2015
comment
и это один из примеров, когда его использование вполне правомерно. Нет, это не так. В этом весь смысл. A. Это допустимо только в том случае, если octreenode тривиально разрушаемо B. Даже в этом случае это бесполезно сложно, поскольку codegens в /точно/один и тот же объектный код. Задание в моем ответе проще, правильнее и точно так же эффективно. Итак, ответ более применим :) - person sehe; 15.01.2015
comment
Даже не удаленно. Ваш исходный ответ назначил указатели в массиве на объекты, которые могут находиться где угодно в куче. Мой назначает их в непрерывной группе. Ты не можешь дать совсем другой ответ, а потом в комментариях буйствовать, что ответ, который ты имел в виду, но не написал, более эффективен, и только после того, как я ответил что касается вашего оригинального ответа! Новые пункты в вашем обновлении совершенно верны, но смешно злиться на меня за то, что я отвечаю на то, что вы изначально написали, прежде чем вы добавили их позже, а затем ведете себя так, как будто они были там все время. - person Riot; 15.01.2015
comment
@Riot Нет, мой первоначальный ответ не сделал ничего подобного (это должно быть сразу ясно для любого программист на C++, так как я никогда не использовал выделение кучи). Вы просто не представляете, насколько ошибочны ваши рассуждения. В этом отношении один класс, управляющий местоположением резервного хранилища, — multi_array. Это не был бы массив, если бы хранилище не было непрерывным. - person sehe; 15.01.2015
comment
Также. Я обновил свой ответ по вашему конкретному предложению (должен ли я сказать выговор? но это не тот ответ, который вы на самом деле опубликовали, не так ли? и Если вы опубликуете это как ответ, я уверен, что это также будет действительным.). Я уже объяснил, почему я не разместил их изначально. Я не собираюсь возвращать это сейчас. У вас есть история изменений, чтобы проверить факты о ваших претензиях. - person sehe; 15.01.2015
comment
Нет конечно; но в исходном примере я использовал выделение кучи и должен был четко указать, что это было сделано намеренно, поскольку размер данных был порядка гигабайт. Я понял, что ваш первоначальный ответ означает, что вы выделяете каждый октринод в куче и просто сохраняете multi_array указателей в стеке; теперь я перечитываю и понимаю, что неправильно понял ваше намерение, и это не ваша вина, что ответ был неподходящим, потому что я не смог объяснить в своем вопросе причину требования кучи. Я довольно устал спорить о коде, который я написал много лет назад и не стал бы писать сейчас. - person Riot; 15.01.2015
comment
@Riot Хорошо. Справедливо. Я вижу это неверное толкование. Я предпочитаю обсуждать актуальные программы (именно поэтому я стараюсь предоставлять демонстрации в реальном времени). Аргумент выделения кучи не имеет для меня особого смысла, потому что multi_array по определению использует выделение кучи (или что-то еще, что использует ваш распределитель). И вы даже можете heap_world = new world(...) иметь буквально все, что относится к куче. (Введение косвенных ссылок неизменно делает представление менее эффективным. Но я знаю, что это может быть полезно в зависимости от вариантов использования). Ваше здоровье! - person sehe; 15.01.2015
comment
Да, это именно то, что я имел в виду, когда сказал, что это код, который я бы не стал писать сейчас в первую очередь :) Я рад, что мы на одной странице об этом - person Riot; 16.01.2015