【问题标题】:Const correctness in generic functions using iterators使用迭代器的泛型函数中的 const 正确性
【发布时间】:2022-08-09 22:40:35
【问题描述】:

我想编写一个接受序列的通用函数,同时保证不改变所述序列。

template<typename ConstInputIter, typename OutputIter>
OutputIter f(ConstInputIter begin, ConstInputIter end, OutputIter out)
{
  InputIter iter = begin;
  do
  {
    *out++ = some_operation(*iter);
  }while(iter!=end);
  return out;
}

然而,上面的示例仍然可以将任何类型作为ConstInputIterator,而不仅仅是const。到目前为止,其中const 的概念是名义上的。

我如何声明给定的序列不会被这个函数改变?

  • 小点:通常的约定是返回迭代器的成员函数命名为begin()end(),而用于遍历序列的迭代器命名为firstlast
  • 在 c++20 中,它会更容易一些;在 c++17 中,在函数的 cmets 中编写一些文档。 (是的,有办法尝试确保这一点,但总的来说这是不值得的。)
  • 假设迭代器将是 \"normal\" std 的,您可以将模板类型更改为容器类型,然后 begin/first 将是例如typename T::const_iterator
  • 我建议该函数是否可以接受非常量迭代器并不重要,因为它无论如何都不会更改容器。我想有可能复制初始化迭代器(InputIter iter = begin)或比较它们(iter!=end)会改变容器,但我在现实世界中还没有遇到过这样的迭代器。
  • @Peter 如果有人确实插入了非常量迭代器,编译器会发出一种通用的、难以理解的尖叫声,但这似乎通常是可以接受的。

标签: c++ iterator c++17 const-correctness


【解决方案1】:

即使在 C++20 中,也没有通用方法将非const T 上的迭代器强制转换为T const 上的迭代器。特定的迭代器可能具有执行此操作的机制,您可以使用std::cbegin/cend 获取范围以获取 const 迭代器。但鉴于只要一个迭代器,你受制于用户提供的东西。

应用 C++20 约束(要求 iter_value_tconst)是错误的,因为您的函数应该能够在非 const 范围内运行。

【讨论】:

  • 仅限 C++23 说明的概念 constant-iterator 可能会有所帮助。
  • 两个可能都是有效的答案,谢谢分享。然而,我标记了 C++17,因为这些标准对我的情况来说还无法达到。
【解决方案2】:

由于,您可以在每次尊重您的迭代器时使用std::as_const

// ...
*out++ = some_operation(std::as_const(*iter));
// ...

这确保您永远不会通过非const l 值引用访问底层容器的元素。

笔记:如果您无权访问,那么实现您自己的std::as_const 版本非常简单。只要确保在命名空间std 之外声明它。

【讨论】:

  • 据我了解,在实现此类功能时,这至少是一个好习惯。正如@Peter 在问题下指出的那样:如果函数没有改变任何东西,它不一定是 const。您至少可以在某一时刻明确说明预期的 constness,因此它不会成为函数中进一步的问题。
猜你喜欢
  • 2015-08-23
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多