数组指针何时可转换为不同类型的数组指针?

When is a pointer-to-array convertible to a pointer-to-array of different type?

我正在检查 cppreference documentationstd::unique_ptr,并注意到 C++17 似乎进行了一些有趣的更改。特别是,std::unique_ptr<T[]> 的特化现在接受模板参数,而以前只接受 std::unique_ptr::pointer 参数。例如,这里是 std::unique_ptr<T[]>reset 成员函数之一的声明:

template <typename U> void reset(U p);

网站声明:

Behaves the same as the reset member of the primary template, except that it will only participate in overload resolution if either U is the same type as pointer, or pointer is the same type as element_type* and U is a pointer type V* such that V(*)[] is convertible to element_type(*)[].

我假设这样做是为了安全起见 - 你不想对指向派生类型数组的指针执行 delete[] ,该数组被分配给指向其基类型的指针(在C++17 这被标记为已删除)。正如预期的那样,这段代码编译得很好:

#include <type_traits>

struct foo {};
struct bar : public foo {};
static_assert(!std::is_convertible_v<bar(*)[], foo(*)[]>);

然而,有趣的是,下面的 编译,两个 static_assert 都失败了:

#include <type_traits>

struct foo {};
struct bar : public foo {};
static_assert(std::is_convertible_v<bar*(*)[], foo*(*)[]>);
static_assert(std::is_convertible_v<std::unique_ptr<bar>(*)[], std::unique_ptr<foo>(*)[]>);

这是为什么?这个重载会在什么场景下使用?

struct foo {};
struct bar : public foo {};

在这种微不足道的情况下,foo* 的值表示很可能与 bar* 的值表示完全相同,因此 可以 可以在它们的数组之间进行转换。但是,一旦 bar 变得更加复杂,它就不再成立。考虑一下:

struct foo {};
struct qux {};
struct bar : qux, foo {};

现在,bar* 可隐式转换为 foo*,但该转换不保留其精确值:它需要从 quxfoo 的偏移量.

同样,一旦出现虚拟对象、多访问控制级别等,基 class 子对象的地址就会与最派生对象的地址不同。这样的指针甚至可以有不同的大小(1).

这就是为什么只有少数情况下 derived* 可以用作 base* 而无需进行值更改转换。我的猜测是,考虑到有限的情况会使标准复杂化,但没有什么好处,因此根本不允许在此类指针数组之间进行转换。


(1) 这是一个有点晦涩的案例,但如果 C++ 编译器受到设计不当的 C ABI 的限制,它可能会发生。 This answer 讨论了这种情况。

这基本上是 "you can pass a less const-y pointer if it's safe to do so" 的一种表达方式,例如 int* p = /*...*/; unique_ptr<const int []> up; up.reset(p);

对于不同的类型 UVU(*)[] 可(隐式)转换为 V(*)[] 的唯一情况是通过 qualification conversion,即,当您在类型的正确位置添加 const/volatile 时。确切的规则很复杂(因为它们处理任意嵌套 pointers/pointer-to-members/arrays;如果您想知道,请单击 link),但它们基本上只在安全时才允许转换; unique_ptr 的规范随后利用了这一事实,因此它不必重新定义 "safe",但代价是使意图变得更加神秘。