为什么不能推导unique_ptr的模板参数?
Why can't unique_ptr's template arguments be deduced?
当你从 C++17 中获得 class 模板参数推导时,为什么你不能推导 std::unique_ptr 的模板参数?例如,这给了我一个错误:
std::unique_ptr smp(new D);
也就是说 "Argument list of class template is missing"。
模板参数(至少指针类型)不应该是可推导的吗?
any declaration that specifies initialization of a variable and
variable template
让我们看看 new int
和 new int[10]
。这两个 return 和 int*
。无法判断您是否应该使用 unique_ptr<int>
或 unique_ptr<int[]>
。那个就够了,不提供任何演绎指南。
我不打算重复 中的基本原理,我只是想提一下 如何 它的机制,这就是我以为你也在追求。你是对的,如果 unique_ptr
的构造函数看起来只是...
explicit unique_ptr( T* ) noexcept;
...可以推导出 T
。编译器生成的演绎指南可以正常工作。这将是一个问题,就像内森所说明的那样。但是构造函数是这样指定的...
explicit unique_ptr( pointer p ) noexcept;
... 其中别名pointer
指定如下:
pointer
: std::remove_reference<Deleter>::type::pointer
if that
type exists, otherwise T*
. Must satisfy NullablePointer.
该规范实质上意味着 pointer
必须是 __some_meta_function<T>::type
的别名。 ::type
左边的所有内容都是非推导上下文,这就是防止从 pointer
推导 T
的原因。即使 pointer
需要始终是 T*
,这就是如何使这些推导指南失败的原因。仅通过将其设为非推导上下文将阻止从该构造函数生成的任何推导指南的可行性。
所以这是 C++ 早期的副作用,当时标准制定者决定为对象指针和数组指针使用两个不同的 delete
和 delete[]
运算符对象。
在 C++ 的这些现代时代,我们有模板(它们从一开始就不存在)、std::array
(用于固定大小的数组)、初始化器列表(用于静态固定大小的数组)和 std::vector
(对于动态大小的数组),几乎没有人会再需要 delete[]
运算符了。我从未使用过它,如果这个问题的绝大多数读者也没有使用过它,我也不会感到惊讶。
删除 int* array = new int[5];
以支持 auto* array = new std::array<int, 5>;
会简化事情,并且可以安全地将指针转换为 std::unique_ptr
和 std::shared_ptr
。但它会破坏旧代码,到目前为止,C++ 标准维护者一直非常热衷于向后兼容。
不过,没有人会阻止您编写一个小的内联模板包装函数:
template<typename T>
std::unique_ptr<T> unique_obj_ptr(T* object) {
static_assert(!std::is_pointer<T>::value, "Cannot use pointers to pointers here");
return std::unique_ptr<T>(object);
}
当然你也可以创建一个类似的函数shared_obj_ptr()
来创建std::shared_ptr
,如果你真的需要它们,你也可以添加unique_arr_ptr()
和shared_arr_ptr()
.
当你从 C++17 中获得 class 模板参数推导时,为什么你不能推导 std::unique_ptr 的模板参数?例如,这给了我一个错误:
std::unique_ptr smp(new D);
也就是说 "Argument list of class template is missing"。
模板参数(至少指针类型)不应该是可推导的吗?
any declaration that specifies initialization of a variable and variable template
让我们看看 new int
和 new int[10]
。这两个 return 和 int*
。无法判断您是否应该使用 unique_ptr<int>
或 unique_ptr<int[]>
。那个就够了,不提供任何演绎指南。
我不打算重复 unique_ptr
的构造函数看起来只是...
explicit unique_ptr( T* ) noexcept;
...可以推导出 T
。编译器生成的演绎指南可以正常工作。这将是一个问题,就像内森所说明的那样。但是构造函数是这样指定的...
explicit unique_ptr( pointer p ) noexcept;
... 其中别名pointer
指定如下:
pointer
:std::remove_reference<Deleter>::type::pointer
if that type exists, otherwiseT*
. Must satisfy NullablePointer.
该规范实质上意味着 pointer
必须是 __some_meta_function<T>::type
的别名。 ::type
左边的所有内容都是非推导上下文,这就是防止从 pointer
推导 T
的原因。即使 pointer
需要始终是 T*
,这就是如何使这些推导指南失败的原因。仅通过将其设为非推导上下文将阻止从该构造函数生成的任何推导指南的可行性。
所以这是 C++ 早期的副作用,当时标准制定者决定为对象指针和数组指针使用两个不同的 delete
和 delete[]
运算符对象。
在 C++ 的这些现代时代,我们有模板(它们从一开始就不存在)、std::array
(用于固定大小的数组)、初始化器列表(用于静态固定大小的数组)和 std::vector
(对于动态大小的数组),几乎没有人会再需要 delete[]
运算符了。我从未使用过它,如果这个问题的绝大多数读者也没有使用过它,我也不会感到惊讶。
删除 int* array = new int[5];
以支持 auto* array = new std::array<int, 5>;
会简化事情,并且可以安全地将指针转换为 std::unique_ptr
和 std::shared_ptr
。但它会破坏旧代码,到目前为止,C++ 标准维护者一直非常热衷于向后兼容。
不过,没有人会阻止您编写一个小的内联模板包装函数:
template<typename T>
std::unique_ptr<T> unique_obj_ptr(T* object) {
static_assert(!std::is_pointer<T>::value, "Cannot use pointers to pointers here");
return std::unique_ptr<T>(object);
}
当然你也可以创建一个类似的函数shared_obj_ptr()
来创建std::shared_ptr
,如果你真的需要它们,你也可以添加unique_arr_ptr()
和shared_arr_ptr()
.