什么是clang'范围循环分析'诊断一下?

时间:2018-04-27 15:54:35

标签: c++ clang compiler-warnings clang++ copy-elision

背景:

考虑以下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.

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

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

问题:

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

  

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

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

  1. 究竟是哪里&#34;隐藏&#34;复制操作发生了什么?据我所知,我们只是绑定对临时对象的引用,这应该会延长临时对象的生命周期以匹配引用的生命周期。
  2. 即使我们写了for(const auto element : vectorBool)(以便警告消失),我们也应该在C++17's guaranteed copy elision rules下没有复制/移动操作(甚至在使用任何像样的前C ++ 17中也是如此)编译器),所以是关于使省略复制操作变得明显的警告吗?

1 个答案:

答案 0 :(得分:8)

在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会在

中返回引用
for (const auto& e : range_that_returns_references)

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

for (const auto& e : range_that_returns_proxies_or_copies)

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