分配给 gslice_array 会产生运行时错误
assigning to gslice_array gives runtime error
我正在尝试构建一个从 std::valarray<T>
to use my own methods on it. I have encountered a problem about assigning values using operator[]. After a lot of effort, I think I finally detected the problem. While assigning to std::slice_array<T>
does not constitude a problem, assigning to std::gslice_array<T>
派生的 class。
这里是重现问题的代码:
#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;
}
知道如何解决这个问题吗?
如果你使用 g++,你有悬空引用,这会导致未定义的行为和崩溃。
这是 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) {}
其中 gslice_array
的 _M_array
和 _M_index
定义为:
_Array<_Tp> _M_array;
const valarray<size_t>& _M_index; // <------------- here is reference !
gslice_array
的 _M_index
分配给 __gs._M_index->_M_index
。什么是 __gs._M_index
?
它是 gslice
的内部结构,名为 _Indexer
。它使用引用计数器机制
延长寿命本身。 _Indexer
的引用计数器只有在以下情况下才能增加
gslice
被复制(通过复制构造函数或复制赋值运算符),但是 none 这些
在此代码中执行操作。因此,当gslice
作为临时对象被删除时,__gs
的_M_index
也被删除,最后出现悬空引用。
bug在哪里?保留对索引器成员的引用,当删除 gslice
时删除该成员。
如果索引器持有指向 valarray 的指针而不是引用,就不会发生这种情况。
引用的存储不是过渡性的。
作为解决方法,创建 gslice
的 L 值实例如下:
auto b = get_block(3);
std::gslice_array<int> g = v[b];
g = 3;
print();
那么,一切正常。
我正在尝试构建一个从 std::valarray<T>
to use my own methods on it. I have encountered a problem about assigning values using operator[]. After a lot of effort, I think I finally detected the problem. While assigning to std::slice_array<T>
does not constitude a problem, assigning to std::gslice_array<T>
派生的 class。
这里是重现问题的代码:
#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;
}
知道如何解决这个问题吗?
如果你使用 g++,你有悬空引用,这会导致未定义的行为和崩溃。
这是 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) {}
其中 gslice_array
的 _M_array
和 _M_index
定义为:
_Array<_Tp> _M_array;
const valarray<size_t>& _M_index; // <------------- here is reference !
gslice_array
的 _M_index
分配给 __gs._M_index->_M_index
。什么是 __gs._M_index
?
它是 gslice
的内部结构,名为 _Indexer
。它使用引用计数器机制
延长寿命本身。 _Indexer
的引用计数器只有在以下情况下才能增加
gslice
被复制(通过复制构造函数或复制赋值运算符),但是 none 这些
在此代码中执行操作。因此,当gslice
作为临时对象被删除时,__gs
的_M_index
也被删除,最后出现悬空引用。
bug在哪里?保留对索引器成员的引用,当删除 gslice
时删除该成员。
如果索引器持有指向 valarray 的指针而不是引用,就不会发生这种情况。
引用的存储不是过渡性的。
作为解决方法,创建 gslice
的 L 值实例如下:
auto b = get_block(3);
std::gslice_array<int> g = v[b];
g = 3;
print();
那么,一切正常。