为什么缩小转换范围不能防止错误类型的map.insert()失败

Why is narrowing conversion not preventing this map.insert() of an incorrect type from failing?

本文关键字:map insert 失败 类型 错误 转换 缩小 范围 不能 为什么      更新时间:2023-10-16

背景

当将std::pair<uint64_t, uint64_t>插入C++std::map<uint64_t, int>时,编译器和程序都不会抱怨,即使传递的值对于数据类型uint64_t是不可能的。

换句话说,std::pair<uint64_t, uint64_t>(2, -2)缩小转换不起作用,并且默认为映射的类型std::map<uint64_t, int>

代码

当我用g++ -Wall -Wconversion -Wextra -pedantic test/test_wrong_insert.cpp && ./a.out编译和执行以下代码时:

#include<map>
#include<iostream>
void print_map(std::map<uint64_t, int> & m){
std::cout << "The map is now: {";
for (const auto & n: m){
std::cout << '(' << n.first << ',' << n.second << ") ";
}
std::cout << "}n";
}
int main(){
std::map<uint64_t, int> m;
auto ret = m.insert(std::pair<uint64_t, uint64_t>(2,-2));
std::cout << "Tried to insert std::pair<uint64_t, uint64_t>(2,-2). ";
std::cout << "Return: " << ret.second << 'n';
print_map(m);
}

结果

这是输出:

Tried to insert std::pair<uint64_t, uint64_t>(2,-2). Return: 1
The map is now: {(2,-2) }

问题

为什么std::pair<uint64_t,uint64_t> x{-1,-2}不会产生错误,以及如何使其导致错误?

为什么std::pair<uint64_t,uint64_t> x{-1,-2}不产生错误?

这是由构造函数模板引起的,当两个参数都可以用于构造uint64_t(或实例化std::pair的任何类型)的对象时,该模板会参与重载解析。它是std::pair构造函数列表中的重载(3),其模板参数推导导致转换被视为有意(如auto n = uint64_t{-42};static_cast<uint64_t>(-42);),因此没有警告。你对此无能为力,因为构造函数模板的模板参数不能显式给定,正如这里所解释的那样。

[…]如何使其引起错误?

使用std::make_pair,不要依赖模板参数推导:

auto p = std::make_pair<uint64_t, uint64_t>(-42, -42);
//         Be explicit: ^^^^^^^^  ^^^^^^^^

当您使用-Wsign-conversion编译上面的代码段时(重要的是:-Wconversion不会cath它!),它会给您一个警告(显然,添加-Werror会将其视为错误)。

std::map::insert的问题相同,请参阅此处的过载(2)。这将任何可用的给定参数转换为value_type对象,并将其视为调用方的意图进行的任何转换。有趣的是,std::set上的等价成员函数受到了更多的限制。所以这被抓住了:

std::set<uint64_t> s;
s.insert(-42); // complains with -Wsign-conversion