C ++ 11的优点std::array
在专家解释编程时,我想从编译器中获得一些东西。能够在编译使用.at()
的代码时使用[]
时打开默认范围检查。
检查范围违规特别是对于多维数组可能是有益的,因为在这种情况下,范围违规不太可能导致segfault(因为你经常拥有内部数组的内存,所以[5000][-123]
仍然可能指向你拥有的记忆。)
所以我想知道是否有一个开关可以编译成检查范围的机器代码:
const uint32_t dim1=10*1000,dim2=3;
std::array<std::array<int, dim2>, dim1> test_2Darray;
int undefined_value=test_2Darray[dim2-1][dim1-1];
std::cout<<"ouch ("<<undefined_value<<")"<<std::endl;
int ok_value=test_2Darray[dim1-1][dim2-1];
std::cout<<"OK ("<<ok_value<<")"<<std::endl;
// test_2Darray.at(dim2-1).at(dim1-1); -->terminate called after throwing an instance of 'std::out_of_range'
// what(): array::at
如果你问我为什么不切换到.at()
- 我可能需要性能,而且我已经编写了很多代码[]
并且我不够聪明到聪明到做替换1D更不用说2D阵列了。
我使用GCC 4.6
答案 0 :(得分:5)
看起来gcc 4.6附带的数组还没有调试模式。可以理解,因为C ++ 11支持仍然是实验性的。
有一个标志_GLIBCXX_DEBUG
,通常用于打开调试模式。如果你看一下/usr/include/c++/4.6/debug/vector:313,你会看到operator[]
有:
__glibcxx_check_subscript(__n);
现在,这可能是超级邪恶(我的意思是真的邪恶)但看起来我们可以有条件地将它添加到数组中。更改/usr/include/c++/4.6/array的第148-154行:
reference
operator[](size_type __n)
{ return _M_instance[__n]; }
const_reference
operator[](size_type __n) const
{ return _M_instance[__n]; }
为:
reference
operator[](size_type __n)
{
#ifdef _GLIBCXX_DEBUG
__glibcxx_check_subscript(__n);
#endif
return _M_instance[__n];
}
const_reference
operator[](size_type __n) const
{
#ifdef _GLIBCXX_DEBUG
__glibcxx_check_subscript(__n);
#endif
return _M_instance[__n];
}
这意味着您可以像对向量和其他stl调试一样启用对数组的边界检查 - 通过向编译行添加-D_GLIBCXX_DEBUG
。 E.g:
g++ someAwesomeProgram.cpp -D_GLIBCXX_DEBUG
我刚看了一下gcc trunk,显然还没有对_GLIBCXX_DEBUG引用数组:(。http://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/include/std/array
希望它不会太远。我想我们将有足够的安全迭代器以及调试模式中的所有数组。与此同时,这可能是我们的小秘密: - )。
答案 1 :(得分:3)
template<class T, std::size_t N>
T const& at(std::array<T,N> const& arr, std::size_t pos){
#ifndef NDEBUG
// debug versions, automatically range checked
return arr.at(pos);
#else
// release version, unchecked
return arr[pos];
#endif
}
template<class T, std::size_t N>
T& at(std::array<T,N>& arr, std::size_t pos){
typedef std::array<T,N> const& const_array;
// const_cast of the return is safe here because be pass a non-const array
// const_cast for the argument is needed to avoid infinite recursion
return const_cast<T&>(at(const_cast<const_array>(arr), pos));
}
应该做的工作。只需在整个代码库中始终使用at(arr, pos)
。
答案 2 :(得分:2)
您可以模仿您想要的行为:
#include <array>
#include <cassert>
#include <iostream>
#ifndef NDEBUG
template <typename T, std::size_t N>
struct my_array : std::array<T,N> {
T& operator[](std::size_t n) {
assert(n < N);
return (*static_cast<std::array<T,N>*>(this))[n];
}
const T& operator[](std::size_t n) const {
assert(n < N);
return (*static_cast<const std::array<T,N>*>(this))[n];
}
};
#else
// I would use Alias templates here, but isn't supported on my compiler yet!
template <typename T, std::size_t N>
struct my_array : std::array<T,N> {
};
#endif
它与std::array
不完全匹配,但如果它对您很重要,则可以修复。然后将所有对std::array
的引用替换为my_array
,并为调试版本检查范围operator[]
。
(我已经使用模板别名来简化NDEBUG
代码,但实际上我还无法在我的编译器上进行测试)
答案 3 :(得分:2)
与gcc一起提供的标准库实现libstdc++
并不是gcc(如果你愿意,你可以自由使用另一个实现)。
libstdc++
有一个可用于调试-D_GLIBCXX_DEBUG的预处理程序标志,但是您应该注意此调试模式会更改类型的ABI,因此您需要链接到也具有类型的库已启用此调试模式编译。这可能很痛苦。
libc++
是另一个实现(符合C ++ 11标准),它首先针对Clang,但应该适用于任何兼容的编译器。它旨在维护ABI兼容性,无论是否启用调试。但它在OS X之外并不完全稳定(主要是因为语言环境),因此可能无法在您的环境中使用。
请注意,这两个库都是自由软件,因此如果未实施检查,您可以完美地提交补丁。
答案 4 :(得分:0)
我想要类似的东西,因此编写了这个仅用于小标题的工具 允许您执行set(arr,pos,value)和get(arr,pos)以及 可以启用中止,断言或继续进行 崩溃,没有检查。
https://github.com/goblinhack/c-plus-plus-array-bounds-checker
其要点如下(我在github上也有2维和3维示例)
对于调试版本:
#define DEBUG #define ENABLE_ASSERT #define ENABLE_ABORT #include "array_bounds_check.h"
实施的一些细节:
template<class TYPE, std::size_t XDIM>
void set(std::array<TYPE,XDIM>& arr, std::size_t X, TYPE v){
DODEBUG(std::cerr << "set [" << X << "] = " << v << std::endl);
ASSERT(X >= 0)
ASSERT(X < arr.size())
arr[X] = v;
}
template<class TYPE, std::size_t XDIM>
TYPE& get(std::array<TYPE,XDIM> & arr, std::size_t X){
DODEBUG(std::cerr << "get [" << X << "] = ");
ASSERT(X >= 0)
ASSERT(X < arr.size())
DODEBUG(std::cerr << arr[X] << std::endl);
return (arr[X]);
}
如果要包括对set()和get()调用的跟踪,请启用:
#define DEBUG
要在边界上打印断言(并继续):
#define ENABLE_ASSERT
要在断言时调用abort():
#undef ENABLE_ABORT
hth