присвоение gslice_array дает ошибку времени выполнения - PullRequest
0 голосов
/ 24 января 2020

Я пытаюсь создать класс, полученный из std::valarray<T>, чтобы использовать мои собственные методы для него. Я столкнулся с проблемой при назначении значений с помощью оператора [] . После долгих усилий, я думаю, я наконец обнаружил проблему. При назначении на std::slice_array<T> проблем не возникает, назначение на std::gslice_array<T> делает.

Вот код для воспроизведения проблемы:

#include <valarray>
#include <cassert>
#include <iostream>

int main() {
    const size_t rows = 16;
    const size_t cols = 24;
    assert(rows%8 == 0);
    assert(cols%8 == 0);

    // return b_th 8x8 block
    auto get_block = [&rows, &cols](size_t b) -> std::gslice {
        return std::gslice((b / (cols/8))*8*cols + (b % (cols/8))*8, {8, 8}, {cols, 1});
    };

    // zeros(rows, cols) but 1D
    std::valarray<int> v(0, rows*cols);

    auto print = [&rows, &cols, &v]() -> void {
        for (size_t i=0; i<rows; i++) {
            for (size_t j=0; j<cols; j++)
                std::cout << " " << v[i*cols + j];
            std::cout << "\n";
        }
        std::cout << std::endl;
    };
    print();

    // this is OK
    v[get_block(1)] = 1;
    print();

    // this is also OK
    std::slice_array<int> s = v[std::slice(2*cols, cols, 1)];
    s = 2;
    print();

    // ???
    std::gslice_array<int> g = v[get_block(3)];
//  g = 3;  // this line causes runtime error
    print();

    return 0;
}

Есть идеи, как решить эту проблему?

1 Ответ

1 голос
/ 24 января 2020

Если вы используете g ++, у вас есть свисающая ссылка, которая ведет к неопределенному поведению, и cra sh.

Это ошибка в g ++.

В этой строке:

std::gslice_array<int> g = v[get_block(3)];

путем вызова get_block(3) создается временный экземпляр gslice. Метод valarray::operator[](gslice) вызывается с использованием этого временного gslice. Давайте посмотрим на его реализацию:

template<typename _Tp>
inline gslice_array<_Tp>
valarray<_Tp>::operator[](const gslice& __gs)
{
  return gslice_array<_Tp> (_Array<_Tp>(_M_data), __gs._M_index->_M_index);
}

он принимает ссылку на const gslice, поэтому временный экземпляр gslice может быть связан с __gs, при возврате из этого метода объект gslice_array создается:

  template<typename _Tp>
    inline
    gslice_array<_Tp>::gslice_array(_Array<_Tp> __a,
                    const valarray<size_t>& __i)
    : _M_array(__a), _M_index(__i) {}

, где _M_array и _M_index из gslice_array определены как:

  _Array<_Tp>              _M_array;
  const valarray<size_t>&  _M_index; // <------------- here is reference !

Для _M_index из gslice_array назначено __gs._M_index->_M_index , Что такое __gs._M_index? Это внутренняя структура gslice с именем _Indexer. Он использует механизм счетчика ссылок, чтобы продлить саму жизнь. Счетчик ссылок _Indexer может быть увеличен только в том случае, если копируется gslice (конструктором копирования или оператором назначения копирования), но ни одна из этих операций не выполняется в этом коде. Следовательно, когда gslice как временный объект удаляется, _M_index из __gs также удаляется, и, наконец, появляется свисающая ссылка.

Где ошибка? Сохранение ссылки на член индексатора, который удаляется при удалении gslice. Этого бы не произошло, если бы индексатор содержал указатель на valarray вместо ссылки. Хранение ссылок не является переходным.

В качестве обходного пути, создайте экземпляр значения L gslice следующим образом:

auto b = get_block(3);
std::gslice_array<int> g = v[b];
g = 3;
print();

, тогда все работает нормально.

...