【问题标题】:Why can't unique_ptr's template arguments be deduced?为什么不能推导出unique_ptr的模板参数?
【发布时间】:2018-12-09 02:57:00
【问题描述】:

当你有 C++17 中可用的类模板参数推导时,为什么你不能推导 std::unique_ptr 的模板参数?例如,这给了我一个错误:

std::unique_ptr smp(new D);

说“类模板的参数列表丢失”。

模板参数(至少是指针类型)不应该是可推导的吗?

See this:

任何指定变量初始化的声明和 变量模板

【问题讨论】:

  • 不是答案,只是想知道你为什么不首先使用make_unique..?
  • @Someprogrammerdude C++17 添加了类模板推导指南,使用构造函数调用来推导类的模板类型。
  • @Someprogrammerdude 那么引用的措辞是不是对 cppreference 有点误导? (即将用引用更新问题:))
  • @JesperJuhl 对我来说,我想使用 struct 聚合初始化程序。 make_unique 好像不支持。

标签: c++ templates c++17 template-argument-deduction


【解决方案1】:

让我们看看new intnew int[10]。这两个都返回int*。无法判断您是否应该拥有unique_ptr<int>unique_ptr<int[]>。没有提供任何类型的扣除指南就足够了。

【讨论】:

  • 我明白了其中的道理,但是这不是有一些规则吗?比如,你必须明确定义模板参数 if....
  • 哦,所以让它默认使用unique_ptr<int>,如果你想要这个数组,那么你必须明确地这样做吗?我认为这会更麻烦记住然后总是告诉它你想要什么。
  • 如果这是唯一的意图,那么最好多一个像std::unique_array_ptr这样的类,并有更方便的std::unique_ptr,而不是有一些不方便使用的东西
  • @DmytroOvdiienko auto foo = make_unique<some_type>(some_initializer) 有什么不便之处?
  • 我需要std::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])
【解决方案2】:

我不打算重复@NathanOliver's great answer 中的基本原理,我只是要提一下它的如何,即机制,我认为这也是你所追求的。你是对的,如果unique_ptr 的构造函数看起来只是......

explicit unique_ptr( T* ) noexcept;

...可以推导出T。编译器生成的推导指南可以正常工作。就像内森所说明的那样,这将是一个问题。但是构造函数是这样指定的...

explicit unique_ptr( pointer p ) noexcept;

...其中别名pointer指定如下:

pointer : std::remove_reference<Deleter>::type::pointer 如果那样的话 类型存在,否则T*。必须满足NullablePointer

该规范实质上意味着pointer 必须是__some_meta_function<T>::type 的别名。 ::type 左边的所有内容都是非推导上下文,这就是防止从 pointer 推导 T 的原因。即使pointer 需要始终为T*,这就是这些类型的演绎指南可能会失败的原因。仅将其设为非演绎上下文将阻止从该构造函数生成的任何演绎指南的可行性。

【讨论】:

  • 正是我正在寻找的答案。谢谢。
【解决方案3】:

因此,这是 C++ 早期的一个副作用,当时标准制定者决定使用两个不同的 deletedelete[] 运算符,用于指向对象的指针和指向对象数组的指针。

在现代 C++ 时代,我们有模板(它们从一开始就不存在)、std::array(用于固定大小的数组)、初始化器列表(用于静态固定大小的数组)和std::vector(用于动态大小的数组),几乎没有人需要delete[] 运算符了。我从未使用过它,如果这个问题的绝大多数读者也没有使用过它,我也不会感到惊讶。

删除 int* array = new int[5]; 以支持 auto* array = new std::array<int, 5>; 会简化事情,并且可以安全地转换指向 std::unique_ptrstd::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_ptrs,如果你真的需要,也可以添加unique_arr_ptr()shared_arr_ptr()

【讨论】:

  • new std::array&lt;...&gt; 不适用于动态大小的数组,new std::vector&lt;...&gt; 会浪费分配空间。
  • auto var = new TYPE[N](带有非常数N)的替换是auto var = std::vector&lt;TYPE&gt;(N)。因此不会浪费任何分配,只需要额外的向量数据结构内向量的当前使用和分配大小的空间。对于非平凡的析构函数,这两个值之一,即数组大小,也被保存在隐藏字段中,以便操作员delete [] 正确销毁。如果您担心浪费当前大小的其他 8 个字节,请在标准库中添加不可调整大小的向量。
  • auto var = std::vector&lt;TYPE&gt;(N) 本质上是一个唯一的数组。没有共享或非拥有的对应物。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 2017-05-20
相关资源
最近更新 更多