C++ 为什么允许共享\u ptr<;T[N]>;?

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(*)[]

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(*)[]
应可转换为
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();
    }
}