C++ 为什么允许共享\u ptr<;T[N]>;?
cites,这表明即将对C++ 为什么允许共享\u ptr<;T[N]>;?,c++,stl,shared-ptr,c++17,C++,Stl,Shared Ptr,C++17,cites,这表明即将对std::shared_ptr进行的更改将允许T[]和T[N]变体: 与数组的unique_ptr部分专门化不同,shared_ptr和shared_ptr都将有效,并且都将导致在对象的托管数组上调用delete[] 模板显式共享ptr(Y*p); 要求:Y应为完整类型。表达式delete[]p,当T是数组类型时,或delete p,当T不是数组类型时,应格式良好,行为定义明确,且不应引发异常。当T为U[N]时,Y(*)[N]应可转换为T*;当T为U[]时,Y(*)[]
std::shared_ptr
进行的更改将允许T[]
和T[N]
变体:
与数组的unique_ptr
部分专门化不同,shared_ptr
和shared_ptr
都将有效,并且都将导致在对象的托管数组上调用delete[]
模板显式共享ptr(Y*p);
要求:Y
应为完整类型。表达式delete[]p
,当T
是数组类型时,或delete p
,当T
不是数组类型时,应格式良好,行为定义明确,且不应引发异常。当T
为U[N]
时,Y(*)[N]
应可转换为T*
;当T
为U[]
时,Y(*)[]
应可转换为T*
;否则,Y*
应可转换为T*
除非我弄错了,否则只能通过获取数组的地址来形成Y(*)[N]
,而共享\u ptr
显然不能拥有或删除数组的地址。我也没有看到任何迹象表明,N
以任何方式用于强制管理对象的大小
允许T[N]
语法背后的动机是什么?它是否产生任何实际效益?如果是,它是如何使用的
除非我弄错了,否则只能通过获取地址来形成Y(*)[N]
显然不能被共享的\u ptr
拥有或删除的数组
不要忘记,shared\u ptr
是一个通用的资源管理实用程序,可以使用自定义deallocator构建:
template<class Y, class D> shared_ptr(Y* p, D d);
模板共享(Y*p,D);
这样一个用户提供的deallocator可以执行除delete
/delete[]
之外的操作。例如,如果所讨论的数组是一个文件描述符数组,“deallocator”可以关闭所有文件描述符
在这种情况下,
shared\u ptr
并不拥有广泛使用的对象,因此可以通过获取其地址绑定到现有数组。您可以获得一个指向嵌套对象的指针,该嵌套对象使用std::shared\u ptr
共享包含对象的所有权。如果这个嵌套对象恰好是一个数组,并且您希望以数组类型访问它,那么您实际上需要将T[N]
与合适的T
和N
一起使用:
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <queue>
#include <utility>
#include <vector>
using queue = std::queue<std::function<void()>>;
template <typename T>
struct is_range {
template <typename R> static std::false_type test(R*, ...);
template <typename R> static std::true_type test(R* r, decltype(std::begin(*r))*);
static constexpr bool value = decltype(test(std::declval<T*>(), nullptr))();
};
template <typename T>
std::enable_if_t<!is_range<T>::value> process(T const& value) {
std::cout << "value=" << value << "\n";
}
template <typename T>
std::enable_if_t<is_range<T>::value> process(T const &range) {
std::cout << "range=[";
auto it(std::begin(range)), e(std::end(range));
if (it != e) {
std::cout << *it;
while (++it != e) {
std::cout << ", " << *it;
}
}
std::cout << "]\n";
}
template <typename P, typename T>
std::function<void()> make_fun(P const& p, T& value) {
return [ptr = std::shared_ptr<T>(p, &value)]{ process(*ptr); };
// here ----^
}
template <typename T, typename... M>
void enqueue(queue& q, std::shared_ptr<T> const& ptr, M... members) {
(void)std::initializer_list<bool>{
(q.push(make_fun(ptr, (*ptr).*members)), true)...
};
}
struct foo {
template <typename... T>
foo(int v, T... a): value(v), array{ a... } {}
int value;
int array[3];
std::vector<int> vector;
};
int main() {
queue q;
auto ptr = std::make_shared<foo>(1, 2, 3, 4);
enqueue(q, ptr, &foo::value, &foo::array, &foo::vector);
while (!q.empty()) {
q.front()();
q.pop();
}
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用queue=std::queue;
模板
结构是_范围{
模板静态标准::假_型试验(R*,…);
模板静态std::true_类型测试(R*R,decltype(std::begin(*R))*);
静态constexpr bool value=decltype(test(std::declval(),nullptr));
};
模板
std::如果值>进程(t常量和值),则启用值{
std::没有任何东西可以防止误用,但是一旦你有了一个共享的\u ptr
,你就可以在没有额外信息的情况下迭代它的元素,就是这样。请注意,你仍然用指向第一个元素的指针来构造它,而不是用指向数组本身的指针,因为你应该从new T[N]获取指针
@KerrekSB“…因为假设您从新的T[N]
”获取指针,所以并不总是这样。请参阅。我进一步回顾了N4082,我确实看到操作符[]
将I
作为先决条件,但它也是noexcept
,因此我猜仍然保留ptr[N]
作为未定义的行为。@KerrekSB:实际上,在实际需要std::shared\u ptr
的地方使用带有别名构造函数的数组类型是一种用法。它值得一读,其中包含基本原理和解释。N4082只是将单个建议收集到一个连贯的库基础知识计划中.标准中是否有任何文本证明“shared\u ptr
是通用资源管理实用程序”?正如我一直理解的,shared_ptr
意味着所有权契约。需要允许自定义删除程序意味着不能严格执行该契约,但我一直认为让非所有权的shared_ptr
s@monkey_05\u 06对自定义DealLocator的支持使其成为一个通用的资源管理uti你只知道一个共享指针,最后一个指向一个对象的指针将执行清理操作(默认情况下是删除对象,但原则上可以是任何东西)在删除该引用之前。也许你可以说这是一个语义参数,但是像std::get\u deleter
这样的方法的存在表明delete
操作是shared\u ptr
的一个契约部分(进一步的事实是operator delete
是默认的deleter)。我不是在争论是否可以生成非所有权的shared\u ptr
s,但我坚持认为这样做在实践中是一件坏事。我觉得使用shared\u ptr
作为参考包装会混淆代码。事实上,我刚刚记起来,这将shared\u ptr
的隐含所有权契约又向前推进了一步通过为非拥有的观察者提供一个完整的单独类。也许我错过了您示例的某些部分,但我不明白为什么这个示例需要shared\u ptr
。该示例与shared\u ptr
和shared\u ptr
一样有效(假设后者为std::experimental::shared\u ptr
),唯一的其他修改是让f->array
衰减,而不是使用&
获取其地址。鉴于int(*)[42]
必须衰减为元素类型*
(即int*
),以匹配函数调用,仍然没有明显的好处…@monkey\u 05\u 06:for con
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <queue>
#include <utility>
#include <vector>
using queue = std::queue<std::function<void()>>;
template <typename T>
struct is_range {
template <typename R> static std::false_type test(R*, ...);
template <typename R> static std::true_type test(R* r, decltype(std::begin(*r))*);
static constexpr bool value = decltype(test(std::declval<T*>(), nullptr))();
};
template <typename T>
std::enable_if_t<!is_range<T>::value> process(T const& value) {
std::cout << "value=" << value << "\n";
}
template <typename T>
std::enable_if_t<is_range<T>::value> process(T const &range) {
std::cout << "range=[";
auto it(std::begin(range)), e(std::end(range));
if (it != e) {
std::cout << *it;
while (++it != e) {
std::cout << ", " << *it;
}
}
std::cout << "]\n";
}
template <typename P, typename T>
std::function<void()> make_fun(P const& p, T& value) {
return [ptr = std::shared_ptr<T>(p, &value)]{ process(*ptr); };
// here ----^
}
template <typename T, typename... M>
void enqueue(queue& q, std::shared_ptr<T> const& ptr, M... members) {
(void)std::initializer_list<bool>{
(q.push(make_fun(ptr, (*ptr).*members)), true)...
};
}
struct foo {
template <typename... T>
foo(int v, T... a): value(v), array{ a... } {}
int value;
int array[3];
std::vector<int> vector;
};
int main() {
queue q;
auto ptr = std::make_shared<foo>(1, 2, 3, 4);
enqueue(q, ptr, &foo::value, &foo::array, &foo::vector);
while (!q.empty()) {
q.front()();
q.pop();
}
}