【问题标题】:clang-tidy: `Loop variable is copied but only used as const reference; consider making it a const reference` - does it really matter?clang-tidy: `循环变量被复制但仅用作 const 引用;考虑将其设为 const 引用` - 这真的很重要吗?
【发布时间】:2022-01-08 00:00:28
【问题描述】:

我正在编写 clang-tidy 到处标记的代码

Loop variable is copied but only used as const reference; consider making it a const reference

当前代码:

for (auto foo : collection) {
    ...
}

clang-tidy 建议我使用什么:

for (const auto &foo : collection) {
    ...
}

我当然可以看到 const 引用在哪里比实际复制值更有效,但我想知道编译器是否会以这种方式优化它?我不想一劳永逸地深入研究并更改数百个循环。

【问题讨论】:

  • “如果编译器无论如何都会以这种方式优化它” 如果你将一个非平凡的测试放在一起,利用这两种机制并启动优化器,你可以测试这个通过仔细阅读 asm 的理论。我想你会发现它没有(而且它是正确的,因为它不能不做出一些关于构造、复制等没有副作用的非常深刻的假设)。

标签: c++ reference clang clang-tidy


【解决方案1】:

除了构造新对象之外,构造函数还有副作用。因此,这两个版本的语义可能不同。

即使不是这种情况,编译器也可能无法在编译期间确定。例如,如果foo 类型的复制构造函数没有在同一个翻译单元中定义并且没有使用链接时优化,编译器将无法优化掉复制,因为它可能具有副作用在当前翻译单元的编译时未知。

同样,循环体可能会调用编译器无法证明它们没有修改 foo 的函数,在这种情况下,副本也无法被优化掉。

(顺便说一句,这对于编译器来说比 clang-tidy 给你警告更难证明,因为即使是一个函数,在技术上仍然允许修改它。即使foo 对象本身是const 限定的(例如const auto),函数也可能依赖于foo 对象的地址,而不是容器的foo 对象通过其他程序路径的地址。 )

即使翻译单元中的所有内容都是可见的,并且可观察的行为不依赖于副本,操作也可能过于复杂,编译器无法优化副本。


例子:

auto f(const std::vector<std::string>& x) {
    std::size_t n = 0;
    for(auto y : x)
        n += y.size();
    return n;
}

带有-O3 和 libstdc++ 的 GCC 和 Clang 都不会优化副本(请参阅程序集中的 operator new/operator delete/memcpy 调用):https://godbolt.org/z/d66ac617M

还可以与引用相同的代码进行比较:https://godbolt.org/z/Tdc3GhcEv


但是,在上面的示例中,std::string 的标准库实现可能仍然存在可见性问题。这可能是一个更好的示例,其中所有定义都可能在当前翻译单元中可见:

struct A { int a[64]; };

auto f(const std::vector<std::vector<A>>& x) {
    std::size_t n = 0;
    for(auto y : x)
        n += y.size();
    return n;
}

在这种情况下,GCC 仍然不会优化副本,而 Clang 会这样做,尽管由于某种原因它仍然会在那里留下一些分配大小检查:https://godbolt.org/z/8KWPTx4o5

但是如果类型变得更复杂,那么即使所有内容都是可见的,也可能会出现两个编译器都无法优化副本的点。


如果您知道foo 的类型很简单,例如像 std::vector&lt;int&gt; 容器上的标量,那么它在性能方面并不重要,假设可观察的行为是相同的。

您可能仍想使用 const 引用,更具体地说是 const 部分,以保持整个代码的 const 正确性。这有多么必要,但可能会进入基于意见的领域。

【讨论】:

  • 谢谢;很好的答案。以前从未听说过 godbolt.org;这看起来像一个有用的工具。看来我要回去听听clang-tidy的建议了。
  • 不同意最后的评论。即使它是一个标量,或者一个可简单复制的类型,const 的正确性也很重要。它告诉编译器和未来的代码阅读器你不想/打算修改任何东西。
  • @Taekahn 我已经扩展了我的评论。
猜你喜欢
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 2019-09-27
  • 2020-09-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多