对从引用到 std::reference_wrapper 或编译器错误的隐式转换的理解不正确?

Incorrect understanding of implicit conversion from reference to std::reference_wrapper or compiler bug?

我对一些涉及引用和 std::reference_wrapper 的代码感到困惑,而且我完全不清楚这是否是我的错,因为我误解了引用包装器的工作方式,或者我遇到了编译器错误。

我有一个简单的映射,将一个对象的引用从一个复杂的 class 配对到一个值:

std::unordered_map<Object const &obj, int32_t value> myMap;

(为简单起见,我明确省略了映射编译所需的散列和相等仿函数)

因为我不能直接使用地图中的引用,所以我使用了一个引用包装器:

std::unordered_map<std::reference_wrapper<const Object> obj, int32_t value> myMap;

现在,我的地图填充在一个函数中,例如:

void myFunction(Object const &obj, int32_t value)
{
    ...
    myMap.emplace(std::make_pair(obj, value));
}

但是该代码虽然可以编译,但不起作用。但是,这个按预期工作:

void myFunction(Object const &obj, int32_t value)
{
    ...
    myMap.emplace(std::pair<std::reference_wrapper<const Object>, int32_t>(obj, value));
}

(请注意,在第二个版本中,我仍然没有明确构建 obj 的引用包装器)

所以,我的疑问是:

我对引用包装器的用法有什么误解吗?没有从引用到 reference_wrapper 的隐式转换?如果不是,为什么代码首先编译?

或者这是 std::make_pair 中的已知 issue/flaw 无法正确扣除传递给它的类型?

std::make_pair 从不 (1) 将引用类型推导为对中的一种类型,它总是产生值类型。所以对 emplace 的调用接收一个 temporary std::pair<Object, int32_t> 作为参数。然后从这个临时文件初始化引用包装器,这意味着它将绑定到这个 temporary 对的 first 成员。

在第二种情况下,临时文件的类型为 std::pair<std::reference_wrapper<const Object>, int32_t>(因为您已明确要求),这意味着临时文件对中有一个引用包装器,直接绑定到 obj .然后从这个临时引用包装器复制映射内的引用包装器,因此直接引用 obj.

(1) "Never" 在这里不是真的。有一个例外:当 std::make_pair 的参数之一是 std::reference_wrapper<T> 类型时,该对中的类型将被减为 T&。这实际上为您提供了正确的解决方案:在 make_pair 调用中将 obj 包装在 std::cref 中:

myMap.emplace(std::make_pair(std::cref(obj), value));

这将导致临时对包含引用,并且映射中的 reference_wrapper 将绑定到其引用的对象 (obj),这正是您想要的。