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

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

背景:

考虑以下 example

#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.

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

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

问题:

根据 https://reviews.llvm.org/D4169,警告在以下时间发出:

for (const Foo &x : Foos), where the range Foos only return a copy. Suggest using the non-reference type so the copy is obvious

我完全理解 std::vector<bool> 的迭代器 return 代理类型的副本(而不是引用),但我不同意“所以副本很明显”的说法:

  1. 那个“隐藏的”复制操作到底发生在哪里?据我所知,我们只是将引用绑定到一个临时对象,这应该会延长临时文件的生命周期与参考文件的生命周期相匹配。
  2. 即使我们写了 for(const auto element : vectorBool)(这样警告就会消失),我们也不应该在 下进行 copy/move 操作(甚至在 C++17 之前的版本中)当使用任何像样的编译器时),所以 这个关于使 删除 复制操作明显的警告是吗?!

在 C++17 中,基于范围的 for 循环定义为

{
    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 returns 参考所以

for (const auto& e : range_that_returns_references)

e 可以消除,我们可以只使用范围中的元素。在

for (const auto& e : range_that_returns_proxies_or_copies)

e无法消除。 *__begin 将创建一个代理或副本,然后我们将临时绑定到 e。这意味着在每次迭代中,您都有一个正在创建和销毁的对象,这可能代价高昂,并且在您使用引用时并不明显。该警告希望您使用非引用类型,以表明您实际上并未使用该范围内的元素,而是使用它的 copy/proxy。