叮叮当当的'range-loop-analysis'诊断是关于什么的?

What is clang's 'range-loop-analysis' diagnostic about?

本文关键字:什么 range-loop-analysis 诊断 叮叮当当 于什么      更新时间:2023-10-16

背景:

考虑以下示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<bool> vectorBool{false, true};
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
    return 0;
}

发出警告:

test.cpp:6:21: warning: loop variable 'element' is always a copy because the range of type 'std::vector<bool>' does not return a reference [-Wrange-loop-analysis]
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
                    ^
test.cpp:6:9: note: use non-reference type 'std::_Bit_reference'
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
        ^~~~~~~~~~~~~~~~~~~~~
1 warning generated.

使用range-loop-analysis诊断启用clang进行编译时:

$ clang++ -Wrange-loop-analysis -o test test.cpp

问题:

根据https://reviews.llvm.org/d4169,警告发出时:

for(const foo&amp; x:foos(,其中范围的foos只返回副本。建议使用非参考类型,以便副本显而易见

我完全理解std::vector<bool>的迭代器返回代理类型的副本(而不是参考(,但我不同意"副本",因此副本很明显:

  1. 确切的是"隐藏"复制操作发生?,据我所知,我们只是在绑定了对临时对象的引用,这应该延长临时寿命以匹配参考的物体。
  2. 即使我们已经撰写了for(const auto element : vectorBool)(警告消失(,我们也应该在C 17的保证副本规则下(甚至在使用任何体面的编译器时,甚至在Pre-C 17(下都没有副本/移动操作(,因此这是关于使 exided 复制操作明显的警告?!

C 17中的一个基于循环的范围定义为

{
    auto && __range = range_expression ; 
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
    } 
}

range_declaration = *__begin;

是范围变量初始化的点。通常*__begin返回参考,因此在

for (const auto& e : range_that_returns_references)

e可以消除,我们可以与该范围的元素一起使用。在

for (const auto& e : range_that_returns_proxies_or_copies)

e无法阐明。*__begin将创建代理或复制,然后将该临时绑定到e。这意味着在每次迭代中,您都有一个正在创建和破坏的对象,这可能是昂贵的,并且在使用参考时并不明显。警告希望您使用非参考类型来使您显然不是在与该范围内的元素一起工作,而是与该元素一起使用。