当你在C++17中使用类模板参数推断时,为什么无法推断std::unique_ptr的模板参数?例如,以下代码会导致编译错误:
std::unique_ptr smp(new D);
这表示“类模板的参数列表缺失”。
模板参数(至少是指针类型)不应该是可推导的吗?
任何指定变量和变量模板初始化的声明
当你在C++17中使用类模板参数推断时,为什么无法推断std::unique_ptr的模板参数?例如,以下代码会导致编译错误:
std::unique_ptr smp(new D);
这表示“类模板的参数列表缺失”。
模板参数(至少是指针类型)不应该是可推导的吗?
任何指定变量和变量模板初始化的声明
让我们看一下 new int
和 new int[10]
。这两者都返回一个 int*
。没有办法确定你应该使用 unique_ptr<int>
还是 unique_ptr<int[]>
。这就足以不提供任何推断指南。
unique_ptr<int>
,如果你想要数组,则必须明确指定?我认为这可能更麻烦,因为你需要记住什么情况下需要特别指定,而不是直接告诉它你需要什么。 - NathanOliverstd::unique_ptr
来跟踪我的数组指针。std::make_unique
用零初始化数组。std::make_unique_for_overwrite
将在C++20中可用。因此,如果我想创建unique_ptr
,我必须两次写入类型std::unique_ptr<char[]>(new char[128])
而不是std::unique_ptr(new char[128])
。 - Dmytro Ovdiienkostd::vector
的原因呢? - NathanOliverstd::unique_ptr
的二元构造函数时,删除器(deleter)的类型至少可以被推断出来,对吗? - user2023370我不会重复@NathanOliver的优秀答案中的原理,我只会提到它的实现方式,机制,这也是我认为你想知道的。如果unique_ptr
的构造函数仅仅是这样的话,你是对的...
explicit unique_ptr( T* ) noexcept;
如果我们能够推断出T
,那么编译器生成的推断指南就可以正常工作。这将是一个问题,正如Nathan所示。但构造函数的规定如下...
explicit unique_ptr( pointer p ) noexcept;
...其中别名pointer
的指定如下:
pointer
:如果该类型存在,则为std::remove_reference<Deleter> :: type :: pointer
,否则为T *
。必须满足NullablePointer。
这个规范实际上意味着pointer
必须是__some_meta_function<T> :: 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()
。new std::array<...>
不能用于动态大小的数组,而 new std::vector<...>
则会浪费一次分配。 - n. m.auto var = new TYPE[N]
(其中 N
不是常量)的替代方法是 auto var = std::vector<TYPE>(N)
。因此,不会浪费任何分配,只需要额外的空间来存储向量数据结构中当前使用和已分配大小的空间。对于非平凡析构函数,这两个值之一,即数组大小,也会在隐藏字段中保留以便由 delete []
运算符进行正确的销毁。如果担心浪费另外八个字节的当前大小,则请将不可调整大小的向量添加到标准库中。 - Kai Petzkeauto var = std::vector<TYPE>(N)
本质上是一个独特的数组。没有共享或非拥有的对应物。 - n. m.
make_unique
呢..? - Jesper Juhlmake_unique
似乎不支持它。 @JesperJuhl - Yongwei Wu