简体   繁体   English

为什么缩小转换不会阻止不正确类型的map.insert()失败?

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

Background 背景

When inserting a std::pair<uint64_t, uint64_t> to a C++ std::map<uint64_t, int> , neither the compiler nor the program complains, even if the passed values are not possible for the data type uint64_t . std::pair<uint64_t, uint64_t>插入到C ++ std::map<uint64_t, int> ,编译器和程序都不会抱怨,即使数据类型uint64_t无法传递值也是如此。

In other words, the narrowing conversion of std::pair<uint64_t, uint64_t>(2, -2) isn't working and is defaulting to the map's type std::map<uint64_t, int> 换句话说, std::pair<uint64_t, uint64_t>(2, -2)缩小转换不起作用,并且默认为地图的类型std::map<uint64_t, int>

Code

When I compile and execute the following code with g++ -Wall -Wconversion -Wextra -pedantic test/test_wrong_insert.cpp && ./a.out : 当我使用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);
}

Result 结果

... this is the output: ......这是输出:

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

Question

Why does std::pair<uint64_t,uint64_t> x{-1,-2} not produce an error, and how do I make it cause an error? 为什么std::pair<uint64_t,uint64_t> x{-1,-2}不会产生错误,如何使其导致错误?

Why does std::pair<uint64_t,uint64_t> x{-1,-2} not produce an error? 为什么std::pair<uint64_t,uint64_t> x{-1,-2}不会产生错误?

This is caused by a constructor template that participates in overload resolution when both arguments can be used to construct objects of uint64_t (or whatever type you instantiated std::pair with). 这是由参与重载解析的构造函数模板引起的,当两个参数都可以用于构造uint64_t对象(或者您实例化std::pair with的任何类型)。 It is overload (3) in the list of std::pair constructors , and its template argument deduction leads to a conversion that is taken as intentional (as in auto n = uint64_t{-42}; or static_cast<uint64_t>(-42); ) - hence no warning. 它是std::pair 构造函数列表中的重载(3),其模板参数推导导致转换为有意转换(如auto n = uint64_t{-42};static_cast<uint64_t>(-42); ) - 因此没有警告。 There's not much you can do about it, as the template parameters of a constructor template can't be given explicitly, as explained here . 您无法做到这一点,因为无法明确给出构造函数模板的模板参数,如此处所述

[...] how to make it cause an error? [...]如何使它导致错误?

Use std::make_pair and don't rely on template argument deduction: 使用std::make_pair并且不依赖于模板参数推导:

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

When you compile the above snippet with -Wsign-conversion (important: -Wconversion won't cath it!), it will give you a warning (obviously, add -Werror to treat it as an error). 当您使用-Wsign-conversion编译上述代码段时(重要: -Wconversion不会-Wconversion它!),它会给您一个警告(显然,添加-Werror将其视为错误)。

The issue with std::map::insert is the same, see overload (2) here . 这个问题std::map::insert是一样的,(2)见超载这里 This turns any usable given argument into a value_type object, and treats it as any conversion would be the caller's intention. 这将任何可用的给定参数转换为value_type对象,并将其视为任何转换将是调用者的意图。 Interestingly, the equivalent member function on a std::set is more restricted. 有趣的是, std::set上的等效成员函数更受限制。 So this is caught: 所以这被抓住了:

std::set<uint64_t> s;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM