背景
当将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