铿锵的"范围 - 循环 - 分析"诊断是什么?

Mik*_*ike 5 c++ clang compiler-warnings copy-elision clang++

背景:

请考虑以下示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<bool> vectorBool{false, true};
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它会发出警告:

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.
Run Code Online (Sandbox Code Playgroud)

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

$ clang++ -Wrange-loop-analysis -o test test.cpp
Run Code Online (Sandbox Code Playgroud)

问题:

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

for(const Foo&x:Foos),其中范围Foos仅返回副本.建议使用非引用类型,因此副本很明显

我完全理解std::vector<bool>迭代器返回代理类型的副本(而不是引用),但我不同意语句"所以副本很明显":

  1. 究竟哪个"隐藏"的复制操作发生了?据我所知,我们只是绑定对临时对象的引用,这应该延长临时对象的生命周期以匹配引用的生命周期.
  2. 即使在情况下,我们已经写了for(const auto element : vectorBool)(这样的警告消失),我们应该在任何复制/移动操作C++ 17的保证复制省略规则(即使是在预先C++ 17使用任何像样的编译时和),所以是关于使删除复制操作明显的警告?!

Nat*_*ica 8

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

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

range_declaration = *__begin;
Run Code Online (Sandbox Code Playgroud)

是范围变量初始化的点.通常*__begin返回一个引用

for (const auto& e : range_that_returns_references)
Run Code Online (Sandbox Code Playgroud)

e可以消除,我们可以使用范围内的元素.在

for (const auto& e : range_that_returns_proxies_or_copies)
Run Code Online (Sandbox Code Playgroud)

e不能被挽救. *__begin将创建一个代理或副本,然后我们将该临时绑定到e.这意味着在每次迭代中,您都有一个被包装和销毁的对象,这可能代价高昂,而且当您使用引用时并不明显.警告要求您使用非引用类型,以明确表示您实际上没有使用范围中的元素,而是使用它的副本/代理.