首页 > 解决方案 > 为什么缩小转换不能防止这个不正确类型的 map.insert() 失败?

问题描述

背景

将 a 插入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}产生错误,如何让它导致错误?

标签: c++c++11

解决方案


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

这是由参与重载决议的构造函数模板引起的,当两个参数都可用于构造uint64_t(或您实例化的任何类型std::pair)的对象时。std::pair 它在构造函数列表中是重载 (3) ,并且它的模板参数推导导致转换被视为有意的(如auto n = uint64_t{-42};or static_cast<uint64_t>(-42);) - 因此没有警告。您对此无能为力,因为无法明确给出构造函数模板的模板参数,如此处所述

[...]如何使它导致错误?

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

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

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

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

std::set<uint64_t> s;

s.insert(-42); // complains with -Wsign-conversion

推荐阅读