为什么 std::shared_ptr<T> = std::unique_ptr<T[]> 编译,而 std::shared_ptr<T[]> = std::unique_ptr<T[]> 不编译?
Why does std::shared_ptr<T> = std::unique_ptr<T[]> compile, while std::shared_ptr<T[]> = std::unique_ptr<T[]> does not?
我使用以下输入命令在 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<T[]>.operator=(std::unique_ptr<T[], Deleter>&&)
的第 4 次重载,以表明这种语法是合法的——T[]
和 T[]
是毕竟,无论 std::shared_ptr
的数组类型的特化状态如何,都是相同的类型。
此外,此语法似乎只适用于 std::make_unique<T[]>
的乘积,而不适用于唯一的指针对象,这与我对该主题的理解不符——调用实际上不应该相同,尽管一个移动现有对象,而另一个移动刚刚创建的对象?我希望它们之间的唯一区别是第一种情况下函数调用后的无效 std::unique_ptr<T[]>
。
作为旁注,我假设因为有一种方法可以将动态分配的数组构造成 shared_ptr
而不需要使用 new
,我应该更喜欢它对 new T[N]
?
的更混乱和异常不安全的调用
tl;博士:
operator=
在 std::shared_ptr<T[]>
和 std::unique_ptr<T[]>
之间根本不起作用,尽管我希望它能起作用。为什么?
- 如果有的话,我希望从
T[]
到 T
的类型转换是唯一指针和共享指针之间编译错误的来源。为什么这样做有效?
operator=
在 std::shared_ptr<T>
和 std::make_unique<T[]>
之间有效,但在 std::unique_ptr<T[]>
之间无效。为什么?
- 我假设在需要动态分配的共享数组的情况下是否正确,但我不想使用 boost 或向量(原因如下)我应该调用
operator= std::make_unique<T[]>(N)
?
为什么我没有使用?
- Boost: 我公司还没有批准使用,不知道什么时候能批准使用。
- Arrays: 我必须在运行时确定这个数组的大小。
- Vectors:我正在研究实时信号处理系统,希望避免额外的指针取消引用。我也试图避免在我的头文件中包含无关的库(这是为了读写子系统之间的通信)但是,我最终选择稍后优化它,如果它很重要(过早的优化......)并咬紧牙关。不过,问题仍然存在。
§20.8.2.2.1/28:
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
Remark: This constructor shall not participate in overload resolution
unless unique_ptr<Y, D>::pointer
is convertible to T*
.
然而,unique_ptr<U[]>::pointer
实际上是U*
,而shared_ptr<U[]>
的T*
是U(*)[]
;并且 U*
无法转换为 U(*)[]
,因此从不考虑重载。
我使用以下输入命令在 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<T[]>.operator=(std::unique_ptr<T[], Deleter>&&)
的第 4 次重载,以表明这种语法是合法的——T[]
和 T[]
是毕竟,无论 std::shared_ptr
的数组类型的特化状态如何,都是相同的类型。
此外,此语法似乎只适用于 std::make_unique<T[]>
的乘积,而不适用于唯一的指针对象,这与我对该主题的理解不符——调用实际上不应该相同,尽管一个移动现有对象,而另一个移动刚刚创建的对象?我希望它们之间的唯一区别是第一种情况下函数调用后的无效 std::unique_ptr<T[]>
。
作为旁注,我假设因为有一种方法可以将动态分配的数组构造成 shared_ptr
而不需要使用 new
,我应该更喜欢它对 new T[N]
?
tl;博士:
operator=
在std::shared_ptr<T[]>
和std::unique_ptr<T[]>
之间根本不起作用,尽管我希望它能起作用。为什么?- 如果有的话,我希望从
T[]
到T
的类型转换是唯一指针和共享指针之间编译错误的来源。为什么这样做有效? operator=
在std::shared_ptr<T>
和std::make_unique<T[]>
之间有效,但在std::unique_ptr<T[]>
之间无效。为什么?- 我假设在需要动态分配的共享数组的情况下是否正确,但我不想使用 boost 或向量(原因如下)我应该调用
operator= std::make_unique<T[]>(N)
?
为什么我没有使用?
- Boost: 我公司还没有批准使用,不知道什么时候能批准使用。
- Arrays: 我必须在运行时确定这个数组的大小。
- Vectors:我正在研究实时信号处理系统,希望避免额外的指针取消引用。我也试图避免在我的头文件中包含无关的库(这是为了读写子系统之间的通信)但是,我最终选择稍后优化它,如果它很重要(过早的优化......)并咬紧牙关。不过,问题仍然存在。
§20.8.2.2.1/28:
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
Remark: This constructor shall not participate in overload resolution unless
unique_ptr<Y, D>::pointer
is convertible toT*
.
然而,unique_ptr<U[]>::pointer
实际上是U*
,而shared_ptr<U[]>
的T*
是U(*)[]
;并且 U*
无法转换为 U(*)[]
,因此从不考虑重载。