提问者:小点点

C 宏将 .at() 替换为 []


我经常有一个索引越界错误,如果我使用 .at() 而不是 [] 来访问向量或字符串中的元素,很容易被捕获。但是,由于边界检查,at() 使我的程序减慢了 5 倍。

我正在尝试编写一个宏来将 .at(someVariable) 替换为 [someVariable],这样我就可以取消注释宏,而不是手动将每个 .at() 替换为 []。我已经阅读了有关 cppreference.com 宏的文档,但似乎无法设计出获得此功能的方法。


共3个答案

匿名用户

一般来说,我会避免使用这种宏,因为它们是“不可见的”(因此更改后的语义对任何不知情的人都巧妙地隐藏了),并且可以更改实际期望越界的代码功能,即使在发布版本中也是如此。如果有的话,我会定义一些突出的东西,例如全大写的 AT。

幸运的是,这实际上并不需要,因为“三大”C 运行时已经具有内置功能,可以有条件地启用 operator[] 上的边界检查(这也具有比 at 更具可读性的优点):

  • 如果你使用 g /libstdc 如果你传递 -D_GLIBCXX_DEBUG你会得到 STL 容器的调试版本,它对 operator[] 执行边界检查,以及对迭代器的许多其他调试检查;
  • CLang/libc 可以执行类似的检查,将_LIBCPP_DEBUG设置为 1;
  • 对于 Visual C,启用类似功能时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 成员函数声明,从而导致编译错误。

相关问题