我经常有一个索引越界错误,如果我使用 .at()
而不是 []
来访问向量或字符串中的元素,很容易被捕获。但是,由于边界检查,at()
使我的程序减慢了 5 倍。
我正在尝试编写一个宏来将 .at(someVariable
) 替换为 [someVariable]
,这样我就可以取消注释宏,而不是手动将每个 .at()
替换为 []。
我已经阅读了有关 cppreference.com 宏的文档,但似乎无法设计出获得此功能的方法。
一般来说,我会避免使用这种宏,因为它们是“不可见的”(因此更改后的语义对任何不知情的人都巧妙地隐藏了),并且可以更改实际期望越
界的代码功能,即使在发布版本中也是如此。如果有的话,我会定义一些突出的东西,例如全大写的 AT。
幸运的是,这实际上并不需要,因为“三大”C 运行时已经具有内置功能,可以有条件地启用 operator[]
上的边界检查(这也具有比 at
更具可读性的优点):
-D_GLIBCXX_DEBUG
你会得到 STL 容器的调试版本,它对 operator[]
执行边界检查,以及对迭代器的许多其他调试检查;_LIBCPP_DEBUG
设置为 1;ITERATOR_DEBUG_LEVEL
设置为 2
(默认情况下已在调试版本中启用)。顺便说一下,其中一些错误(那些实际上溢出分配大小的错误,而不仅仅是向量的“逻辑有效”大小)也可以使用地址清理器(-fsanitize=gcc
和clang上的地址)或valgrind(慢!)和类似的工具发现。
这里有一种不涉及宏的方法 - 它使用 c 17 的 if constexpr
来方便:
#include <vector>
constexpr bool safe_mode = true; // or false
template<class Container>
auto at(Container& v, std::size_t i) -> decltype(auto)
{
if constexpr (safe_mode)
return v.at(i);
else
return v[i];
}
int& test(std::vector<int>& v)
{
return at(v, 6);
}
您可以使用数组取消引用运算符的全名:
#define at(x) operator[](x)
(x)
部分不是必需的,但只有在它后面跟着参数时才会在 at
替换,而不是单独替换单词。您还需要在包含所有标准标头后定义它,否则它将替换具有它的类中的 at
成员函数声明,从而导致编译错误。