为什么允许shared_ptr <T [N]>?

mon*_*506 21 c++ stl shared-ptr c++17

这个答案引用了N4082,它表明即将进行的更改std::shared_ptr将允许两者T[]T[N]变体:

不同于unique_ptr用于阵列部分特,都shared_ptr<T[]>shared_ptr<T[N]>将是有效的,都将导致delete[]被称为对象的管理的阵列上.

 template<class Y> explicit shared_ptr(Y* p);
Run Code Online (Sandbox Code Playgroud)

要求:Y应为完整类型.表达式delete[] p,当T是数组类型时,或者delete p,当T不是数组类型时,应该是格式良好的,应该具有良好定义的行为,并且不应抛出异常.如果TU[N],Y(*)[N]应转换为T*; 当TU[],Y(*)[]应转变成T*; 否则,Y*应可转换为T*.

除非我弄错了,Y(*)[N]否则只能通过获取数组的地址来形成,这显然不能被a拥有或删除shared_ptr.我也没有看到任何N以任何方式强制使用托管对象大小的指示.

允许T[N]语法的动机是什么?它是否产生任何实际效益,如果是,它是如何使用的?

Die*_*ühl 6

您可以获取指向嵌套对象的指针,该嵌套对象与std::shared_ptr包含对象的a共享所有权.如果这个嵌套对象碰巧是一个数组而你想以数组类型的形式访问它,那么实际上你需要使用T[N]合适的TN:

#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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码q只是一个简单std::queue<std::function<void()>>但我希望你可以想象它可以是一个线程池卸载处理到另一个线程.实际安排的处理也是微不足道的,但是,我希望你能想象它实际上是一些大量的工作.


Leo*_*eon 2

除非我弄错了,否则 aY(*)[N]只能通过获取数组的地址来形成,而该数组显然不能由 a 拥有或删除shared_ptr

不要忘记这shared_ptr是一个通用的资源管理实用程序,可以使用自定义解除分配器构建:

template<class Y, class D> shared_ptr(Y* p, D d);
Run Code Online (Sandbox Code Playgroud)

delete这样的用户提供的解除分配器可以执行除/之外的操作delete[]。例如,如果所讨论的数组是文件描述符数组,则“释放器”可以关闭所有它们。

在这种情况下,shared_ptr不拥有广泛使用的意义上的对象,因此可以通过获取其地址将其绑定到现有数组。