std::vector
时,有没有办法将std::size_t
类型的索引转换为相应的迭代器,而在启用-Wsign-conversion
(GCC,Clang)时不产生任何警告,最好不使用static_cast
?
将索引转换为迭代器的标准方法似乎假定有符号索引,因此当索引为无符号类型时会生成警告。这是相当不幸的,因为
std::vector
的自然索引类型是无符号类型,使用带符号索引的数据访问将生成符号转换警告。
std::vector::erase
这样的方法需要一个迭代器,并且没有明显的替代方案来代替 erase
直接使用索引。
考虑以下示例:
#include <vector>
#include <iterator>
int main()
{
std::vector<int> data{0, 1, 2, 3, 4};
std::size_t index = 1;
data[index]; // No warning
const auto iterator = std::next(data.begin(), index); // Warning
data.erase(iterator);
return 0;
}
使用 GCC 或 Clang 编译时,使用
-Wsign-conversion
时,会生成警告:
g++ -Wsign-conversion main.cpp
main.cpp:12:55: 警告:从 'std::size_t' {aka 'long unsigned int'} 转换为 'std::__iterator_traits<__gnu_cxx::__normal_iterator
, void>::difference_type' {aka 'long int'} 可能会改变结果的符号 [-Wsign-conversion]
将索引类型从
std::size_t
更改为 int
并不是一个很好的解决方案,因为数据访问 (data[index]
) 然后会生成警告。数据访问当然是更常见的情况。
使用指针运算也无济于事,因为这也会产生符号转换警告:
const auto iterator = data.begin() + index;
起作用的是
static_cast
,尽管这几乎不可取,并且会引发有关可能的范围差异和未定义行为的问题:
const auto iterator = std::next(data.begin(), static_cast<std::ptrdiff_t>(index));
如果通过
erase
中的自然索引类型进行 std::vector
的唯一方法需要 static_cast
(启用时使符号转换警告静音),则感觉这里的 API 设计存在一个大漏洞。
使用指针运算也无济于事,因为这也会产生符号转换警告:
const auto iterator = data.begin() + index;
所以,如果一个人只有一把锤子,其他一切看起来都像钉子:
const auto iterator=data.begin()+static_cast<long>(index);
不再有警告。有时,碳基生命形式知道的更多,并且需要智取编译器。