#c
#c
Вопрос:
У меня довольно странное предупреждение, о котором сообщает clang-tidy 12.0.1. В следующем коде:
#include <vector>
int main()
{
std::vector<int> v1;
const auto a = v1.begin() v1.size();
return 0;
}
Я вижу, что это предупреждение срабатывает:
error: narrowing conversion from 'std::vector<int>::size_type' (aka 'unsigned long long') to signed type 'std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>::difference_type' (aka 'long long') is implementation-defined [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions,-warnings-as-errors]
const auto a = v1.begin() v1.size();
^
Насколько я понимаю, при работе с двумя целыми числами с одинаковым размером, но разной подписью целое число со знаком преобразуется в unsigned, а не наоборот. Я что-то здесь упускаю?
Комментарии:
1. Здесь вы не работаете с двумя целыми числами. Вы передаете целочисленный аргумент без знака для параметра целочисленной функции со знаком.
2. @Evg Я отредактировал свой вопрос с помощью более минималистичного примера. О какой функции вы говорите?
3. переполнение со знаком — это UB
4. evg говорит, что вы вызываете «iterator::operator (long long)», где «long long» — это разностный тип итератора. Таким образом, ваш std::size_t неявно преобразуется в signed long long, и вы получаете это предупреждение clang tidy.
Ответ №1:
Начиная с C 20 простым решением является использование std::sszie
:
const auto a = v1.begin() std::ssize(v1);
Комментарии:
1. Или использовать
v1.end()
. 😉
Ответ №2:
Чтобы показать фактические задействованные типы :
// Operator accepts difference type
// https://en.cppreference.com/w/cpp/iterator/move_iterator/operator_arith
// constexpr move_iterator operator ( difference_type n ) const;
#include <type_traits>
#include <vector>
#include <iterator>
int main()
{
std::vector<int> v1;
auto a = v1.begin();
auto d = v1.size();
// the difference type for the iterator is a "long long"
static_assert(std::is_same_v<long long, decltype(a)::difference_type>);
// the type for size is not the same as the difference type
static_assert(!std::is_same_v<decltype(d), decltype(a)::difference_type>);
// it is std::size_t
static_assert(std::is_same_v<decltype(d), std::size_t>);
return 0;
}
Комментарии:
1. Понятно. Удивительно, как C может превратить даже самые простые задачи в сложные кошмары.
2. Если вы думаете, что это кошмар, вы еще не касались инициализации. 😉 С другой стороны, правила по большей части довольно понятны и их можно посмотреть. И я думаю, что это язык, на котором вы можете иметь очень высокий уровень абстракции, получать низкоуровневый контроль, когда это необходимо, и где вы можете получить детерминированное поведение (если вы остаетесь в стороне от UB).