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

时间:2019-01-30 19:28:48

标签: c++ c++11

背景

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}不产生错误,如何使它引起错误?

1 个答案:

答案 0 :(得分:4)

  

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

这是由构造器模板引起的,该构造器模板可同时使用两个参数来构造uint64_t(或实例化std::pair的任何类型)的对象时参与重载解析。 std::pair constructors列表中的重载(3),其模板参数推导导致转换为故意(如auto n = uint64_t{-42};static_cast<uint64_t>(-42);)-因此没有警告。您无能为力,因为explained here无法明确给出构造函数模板的模板参数。

  

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

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

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

当您使用-Wsign-conversion编译以上代码段时(重要的是-Wconversion不会起作用!),它将发出警告(显然,添加-Werror会将其视为错误)。

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

std::set<uint64_t> s;

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