Boost MultiArray Copy Constructor
I have got a problem understanding the copy constructor implemen开发者_高级运维tation of boost::multi_:array.
When I try the following
std::vector<double> a;
std::vector<double> b;
a.resize(12);
b.resize(10);
a=b;
everything works out fine,
but when I try
boost::multi_array<double,1> a;
boost::multi_array<double,1> b;
a.resize(boost::extents[12]);
b.resize(boost::extents[10]);
a=b;
I get a crash.
I expected the same behaviour, but I also could not find anything useful in the documentation.
Does anyone have an idea ?
Regards
awallrab
It looks like boost::multi_array
works just like std::valarray
regarding assignment, that is the size of the 2 arrays must match.
According to the documentation:
Each of the array types
multi_array
,multi_array_ref
,subarray
, andarray_view
can be assigned from any of the others, so long as their shapes match.
The semantics of the boost::multi_array assignment operators are infuriatingly bad, wasting countless man-hours of developer time searching for answers like this one, and worse yet introducing lord knows how many subtle bugs that have gone undetected in projects all over the world, for reasons that I could best describe as laughable arrogance.
You can fix the assigment operators inside multi_array.hpp thusly:
// replacement for arrogant boost::multi_array assignment semantics
template <typename ConstMultiArray>
multi_array& operator=(const ConstMultiArray& other) {
// deallocate
deallocate_space();
base_ = 0;
allocated_elements_ = 0;
// copy members of const_multi_array_ref
storage_ = other.storage_;
extent_list_ = other.extent_list_;
stride_list_ = other.stride_list_;
index_base_list_ = other.index_base_list_;
origin_offset_ = other.origin_offset_;
directional_offset_ = other.directional_offset_;
num_elements_ = other.num_elements_;
// allocate
allocator_ = other.allocator_;
allocate_space();
// iterator-based copy
std::copy(other.begin(),other.end(),this->begin());
return *this;
}
multi_array& operator=(const multi_array& other) {
if (&other != this) {
// deallocate
deallocate_space();
base_ = 0;
allocated_elements_ = 0;
// copy members of const_multi_array_ref
storage_ = other.storage_;
extent_list_ = other.extent_list_;
stride_list_ = other.stride_list_;
index_base_list_ = other.index_base_list_;
origin_offset_ = other.origin_offset_;
directional_offset_ = other.directional_offset_;
num_elements_ = other.num_elements_;
// allocate
allocator_ = other.allocator_;
allocate_space();
// copy
boost::detail::multi_array::copy_n(other.base_,other.num_elements(),base_);
}
return *this;
}
Contrary to the irrelevant beliefs of the boost authors in this case, this sort of fix will not break anything. The reason it won't break anything is that the behavior of the original library is to straight up crash with an assertion if sizes don't match, whereas the new code will resize the multi_array first and then not crash. So unless you have test cases that require the application to crash, no test cases can possibly be broken by this change.
Saving countless hours of productivity and protecting against countless undetected bugs absolutely trumps any semantic ideologies the authors might have. There. That's my rant. You're welcome.
精彩评论