从 int 到 shared_ptr 的隐式转换

Implicit conversion from int to shared_ptr

考虑以下代码:

#include <iostream>
#include <memory>

void f(std::shared_ptr<int> sp) {}

template <typename FuncType, typename PtrType>
auto call_f(FuncType f, PtrType p) -> decltype(f(p))
{
    return f(p);
}

int main()
{
    f(0); // doesn't work for any other int != 0, thanks @Rupesh
    // call_f(f, 0); // error, cannot convert int to shared_ptr
}

main() 的第一行中,整数 0 被转换为 std::shared_ptr<int> 并且调用 f(0) 成功,没有任何问题。但是,使用模板调用函数会使情况有所不同。第二行将不再编译,错误为

error: could not convert 'p' from 'int' to 'std::shared_ptr<int>'

我的问题是:

  1. 为什么第一次调用成功,第二次不成功?我在这里遗漏了什么吗?
  2. 我也不明白从 intstd::shared_ptr 的转换是如何在调用 f(0) 中执行的,因为它看起来 std::shared_ptr 只有明确的构造函数。

PS:此示例的变体出现在 Scott Meyers 的 Effective Modern C++ 项目 8 中,作为使用 nullptr 保护此类调用的一种方式.

根据 [conv.ptr]/1(此处引用 N4296):

A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t. ... A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t.

shared_ptr 有一个非显式构造函数,它接受 std::nullptr_t 每个 [util.smartptr.shared.const]/1:

constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }

构造一个空的、非拥有的shared_ptr

当你直接调用f(0)时,0是一个空指针常量,被上面的构造函数隐式转换为shared_ptr<int>。当您改为调用 call_f(f, 0) 时,文字 0 的类型被推断为 int,当然 int 不能转换为 shared_ptr<int>.

std::shared_ptr 有一个采用 std::nullptr_t 的构造函数,文字 0 是一个可转换为 std::nullptr_t 来自草案 C++ 标准部分 4.10 [conv.ptr] (强调我的前进):

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. —end note ]

在你的第二种情况下 p 被推断为 int 类型,它虽然值为零,但不再是空指针常量,因此不适合案例.

如T.C。指出用 DR 903 改变了措辞,它需要一个值为零的整数文字,而不是 整数常量表达式 其计算结果为零:

A null pointer constant is an integer literal (2.14.2) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.

第一次调用 f(0) 被编译为 f(nullptr),这对编译器来说很好(但我认为不应该)。第二次调用将为函数创建声明以处理任何 int,这是非法的。

有趣的是,即使这段代码也有效:

f(3-3);
f(3*0);