【问题标题】:Automatically check bounds in std::vector [duplicate]自动检查 std::vector 中的边界 [重复]
【发布时间】:2023-04-03 04:42:01
【问题描述】:

在积极开发使用std::vector 的类期间,经常会发生索引越界的情况。 (有关实际示例,请参见 this code review question。)使用 operator[] 时,这会导致未定义的行为。尽管如此,[] 语法比写.at() 更容易阅读。

因此,我想使用[] 运算符编写我的代码,但同时启用边界检查。测试完代码后,应该很容易去掉边界检查。

我正在考虑以下代码:

util::bound_checked<std::vector<int>> numbers;

numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
std::cout << numbers[17] << "\n";

对我来说,这个实用程序模板似乎非常简单,以至于我希望它存在。可以?如果有,以哪个名字命名?

【问题讨论】:

  • 一些编译器喜欢将at() 作为operator[] 的默认实现并启用_DEUG
  • 哪个编译器?
  • 您的设计有问题,而不是边界检查。向量知道它的大小,幻数是邪恶的。
  • operator[]at 具有不同的语义。选择不是方便或冗长。 at 引发异常,如果其他代码在 catch 块中处理它们,则异常很有用。除了终止当前进程之外,您将如何在如此低的抽象级别上“处理”越界错误?其实at应该被认为是std::vector的设计错误。永远不要使用它。第二件事是:为什么要删除生产中的边界检查?请运送你测试过的东西。

标签: c++ stdvector undefined-behavior


【解决方案1】:

我不认为这样的东西存在,但它很容易创建:

template <class Container>
struct bound_checked : public Container
{
  using Container::Container;

  auto operator[] (typename Container::size_type i) -> decltype(this->at(i))
  { return this->at(i); }

  auto operator[] (typename Container::size_type i) const -> decltype(this->at(i))
  { return this->at(i); }
};

[Live example]

请注意,上面实际上使用了一种不鼓励的做法,即从标准容器(不是为其设计的)的公共继承。我对您的问题的理解是,此包装器仅用于测试目的,这很好。但是,如果您希望它保留在生产环境中并且希望非常安全,请使用非公共继承:

template <class Container>
struct bound_checked : private Container
{
  using Container::Container;

  auto operator[] (typename Container::size_type i) -> decltype(this->at(i))
  { return this->at(i); }

  auto operator[] (typename Container::size_type i) const -> decltype(this->at(i))
  { return this->at(i); }

  using Container::begin;
  using Container::end;
  using Container::at;
  using Container::insert;
  // ... you get the idea
};

【讨论】:

  • 来自this post 似乎不建议这样做。
  • @super 是的,std 容器不适用于公共继承。如果您愿意,可以私下继承并用using 声明填充类。我的印象是,这应该仅用于测试,并且从最终代码中删除了此类的使用。但是,我会在答案中添加免责声明。
【解决方案2】:

对我来说,这个实用程序模板似乎非常简单,以至于我会 期待它存在

对于 gcc,它确实存在。 gcc libstdc++ 有一组调试容器。对于std::vector,它有__gnu_debug::vector 调试容器。见documentation

【讨论】:

    【解决方案3】:

    如果您使用 GCC(可能是 MinGW)、 Clang 与 libstdc++(GCC 的标准库),那么 #define _GLIBCXX_DEBUG 将满足您的需求。

    或者更好的是,用标志定义它:-D_GLIBCXX_DEBUG

    另外,还有_GLIBCXX_ASSERTIONS,它执行的检查更少,但编译(并且可能运行)更快。

    【讨论】:

    • MSVC 在调试版本中自动开启此类行为。
    • @SornelHaetir:如果您指定调试运行时库,MSVC 会这样做,例如与/MTd。没有什么是自动的,除非您的意思是 Visual Studio IDE 会自动在新项目的“调试”配置中添加此编译器选项。
    【解决方案4】:

    是吗?如果有,以哪个名字命名?

    我很确定它确实存在。如果启用了编译器内在 __DEBUG 构建宏,许多编译器会启用 std::vector&lt;T&gt;::at() 作为 std::vector::operator[]() 的实现。

    因此,我想使用 [] 运算符编写代码,但同时启用边界检查。测试完代码后,应该很容易去掉边界检查。

    关于命名,我会说这是 debugrelease 的构建。当代码在没有__DEBUG 定义的情况下编译时,这些检查将被删除。

    【讨论】:

    • 你能说出一个真正做到这一点的编译器吗?我非常怀疑。这意味着下一个catch 处理程序不会崩溃和/或产生核心转储,而是能够捕获产生的异常并继续执行程序。
    • 如果有对实际文档的一些参考支持,这个答案会很好。 “编译器内在”听起来更像是猜测而不是知识。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    相关资源
    最近更新 更多