【问题标题】:Why does std::shared_ptr<T> = std::unique_ptr<T[]> compile, while std::shared_ptr<T[]> = std::unique_ptr<T[]> does not?为什么 std::shared_ptr<T> = std::unique_ptr<T[]> 编译,而 std::shared_ptr<T[]> = std::unique_ptr<T[]> 不编译?
【发布时间】:2015-07-23 16:13:57
【问题描述】:

我使用以下输入命令在 Coliru 中探索了这个主题:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

测试可以找到here,不过我把代码贴在下面了。我在示例中使用了int,因为它是一种基本类型。


#include <iostream>
#include <memory>

struct Foo{
    Foo() :
    a_{0}, b_{1}, c_{-1}, combination_{0.5} {}

    int
        a_,
        b_,
        c_;
    double
        combination_;
};

int main()
{
    //int
    //    *unManagedArray = new int[16];
    std::unique_ptr<int[]>
        uniqueArrayOrigin = std::make_unique<int[]>(16);
    std::shared_ptr<int>
            // works but needs call to new
    //  sharedSingleTest{unManagedArray, std::default_delete<int[]>{}}; 
            // works, does not require call to new
        sharedSingleUnique = std::make_unique<int[]>(16);       
            // compilation error (conversion to non-scalar type)
    //  sharedSingleDerived = uniqueArrayOrigin;                

    //  std::shared_ptr<int[]>
                // compilation errors
    //      sharedArrayTest{unManagedArray, std::default_delete<int[]>{}};
                // compilation error (conversion to non-scalar type)
    //      sharedArrayUnique = std::make_unique<int[]>(16);
                // compilation error (conversion to non-scalar type)
    //      sharedArrayDerived = uniqueArrayOrigin;

    std::shared_ptr<Foo>
            // works: specified overload of operator= for shared_ptr
        nonArrayTest = std::make_unique<Foo>(); 

    std::cout << "done!\n";
}

我在 SO 上四处寻找答案,但只发现 references 没有专业化的 std::shared_ptr 的实施,这主要是因为没有人费心向标准委员会提出适当的建议主题。

我很好奇,因为我会在 cppreference 上解释 operator=std::shared_ptr&lt;T[]&gt;.operator=(std::unique_ptr&lt;T[], Deleter&gt;&amp;&amp;) 的第 4 次重载,以表明这种语法是合法的——T[]T[] 是同一类型,无论其专业化状态如何毕竟,std::shared_ptr 的数组类型。

此外,这种语法似乎只适用于 std::make_unique&lt;T[]&gt; 的乘积,而不是唯一的指针对象,这与我对主题的理解背道而驰——不应该调用实际上是相同的,尽管移动一个现有对象,另一个,嗯,移动刚刚创建的对象?我希望它们之间的唯一区别是在第一种情况下函数调用之后无效的std::unique_ptr&lt;T[]&gt;

作为旁注,我假设由于有一种方法可以将动态分配的数组构造成 shared_ptr,而无需使用 new,我应该更喜欢它而不是更混乱和异常不安全打电话给new T[N]

tl;博士:

  1. operator=std::shared_ptr&lt;T[]&gt;std::unique_ptr&lt;T[]&gt; 之间根本不起作用,尽管我希望它能够起作用。为什么?
  2. 如果有的话,我希望从T[]T 的类型转换是唯一指针和共享指针之间编译错误的来源。为什么会这样?
  3. operator=std::shared_ptr&lt;T&gt;std::make_unique&lt;T[]&gt; 之间有效,但在 std::unique_ptr&lt;T[]&gt; 之间无效。为什么?
  4. 我是否正确假设在需要动态分配的共享数组但我不想使用 boost 或向量的情况下(原因如下)我应该调用operator= std::make_unique&lt;T[]&gt;(N)

我为什么不使用?

  • Boost:我的公司尚未批准使用,我不知道何时或是否会获得批准使用它。
  • 数组:我必须在运行时确定这个数组的大小。
  • Vectors:我正在开发一个实时信号处理系统,并且希望避免额外的指针取消引用。我还试图避免在我的头文件中包含无关的库(这是用于读取和写入子系统之间的通信)但是,我最终选择稍后优化它,如果它很重要(过早的优化......)并咬紧牙关。不过,问题仍然存在。

【问题讨论】:

  • 目前,std::shared_ptr 不支持数组。
  • 澄清一下:不仅仅是不存在数组类型的特化,而是std::shared_ptr 主动不支持数组?我曾假设缺乏特化意味着它对数组类型的作用就像它们对任何其他类型一样。

标签: c++ stl g++ c++14


【解决方案1】:

§20.8.2.2.1/28:

template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r); 

备注:此构造函数不应参与重载决议 除非unique_ptr&lt;Y, D&gt;::pointer 可以转换为T*

然而,unique_ptr&lt;U[]&gt;::pointer实际上是U*,而shared_ptr&lt;U[]&gt;T*U(*)[];并且U* 无法转换为U(*)[],因此从不考虑重载。

【讨论】:

  • @Yakk 因此,因为 T[] 类型的非专业 shared_ptr 的行为不是我所期望的,所以最好简单地将它们视为不支持 T.C.说,而不是简单地缺少一些不错的功能补充?
  • @jaggedSpire 正是如此。还要考虑shared_ptr 从不调用delete[]
  • 为什么当从适当的专用unique_ptr&lt;T[]&gt; 对象提供删除器时,它不调用适当的删除器?另外,我可以进一步询问为什么在现有的unique_ptr&lt;T[]&gt;make_unique&lt;T[]&gt; 的产品中构造有问题的shared_ptr&lt;T&gt; 存在功能差异?
  • @Yakk U(*)[] 不会衰减到 U**,但是如果你有一个 U(*)[] 类型的表达式 u,那么 *u 的类型是 U[],它会衰减到 @ 987654345@和**u(或(*u)[0])可用于访问元素。使用它的最小完整程序,没有任何动态分配,在 TU 1 中是 extern int zero[]; int main() { int (*pzero)[] = &amp;zero; return **pzero; },在 TU 2 中是 int zero[] = { 0 };
  • @jaggedSpire 唯一的区别是从unique_ptr&lt;T[]&gt; 类型的左值赋值和从unique_ptr&lt;T[]&gt; 类型的右值赋值。类型可以选择只允许一个,或者只允许另一个,unique_ptr 是一种利用这一点的类型。左值的赋值通常需要进行复制,unique_ptr 不能被复制,否则指针将不再是唯一的。
猜你喜欢
  • 2018-02-02
  • 2019-10-20
  • 2019-11-07
  • 1970-01-01
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 2020-04-17
  • 1970-01-01
相关资源
最近更新 更多