В чем заключается диагностика clang 'range-loop-analysis'? - PullRequest
0 голосов
/ 27 апреля 2018

Справочная информация:

Рассмотрим следующий пример :

#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, предупреждение выдается, когда:

для (const Foo & x: Foos), где диапазон Foos возвращает только копию. Предложите использовать тип без ссылки, чтобы копия была очевидна

Я полностью понимаю, что итераторы std::vector<bool> возвращают копию типа прокси (вместо ссылки), но я не согласен с утверждением "так что копия очевидна":

  1. Где именно происходит эта «скрытая» операция копирования? Насколько я вижу, мы просто привязываем ссылку к временному объекту, и это должно продлить время жизни временного сопоставления что ссылки.
  2. Даже если мы написали for(const auto element : vectorBool) (чтобы предупреждение исчезло), у нас не должно быть операций копирования / перемещения в соответствии с C ++ 17 гарантированными правилами исключения копирования (и даже в pre-C ++ 17 при использовании любого приличного компилятора), поэтому это предупреждение о том, что операция копирования elided очевидна?!

1 Ответ

0 голосов
/ 27 апреля 2018

В 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. Это означает, что в каждой итерации у вас есть объект, который создается и уничтожается, что может быть дорогостоящим и неочевидным, так как вы используете ссылку. Предупреждение требует, чтобы вы использовали не ссылочный тип, чтобы было очевидно, что вы на самом деле работаете не с элементом из диапазона, а с его копией / прокси.

...