C++ 中的“使用分配器”和“作用域分配器”构造是什么

2024-04-21

有相当多的few https://stackoverflow.com/search?q=uses_allocator 问题 https://stackoverflow.com/search?q=scoped_allocator在这里谈论使用分配器 https://en.cppreference.com/w/cpp/memory/uses_allocator#Uses-allocator_construction and 作用域分配器 https://en.cppreference.com/w/cpp/memory/scoped_allocator_adaptor但它们来自于知道它们是什么以及它们的用途的假设。

通常的 C++ 参考文献中的解释解释了“内容”,但没有解释如何或为什么。

该网站缺少一个规范的答案,该答案将为那些不熟悉该主题的人提供背景知识。


它是一个允许泛型容器类型(广义上的)检测泛型类型参数何时期望与分配器一起使用的协议。

然后,他们可以决定通过自动将外部分配器传播到内部类型来支持这一点。典型的例子是当你想要一个std::map<std::vector<std::string>, std::set<int> >全部使用相同的分配器系列。

包含嵌套的类型allocator_typetypename 将自动被检测为uses_allocator类型,包括所有标准容器。

典型其他uses_allocator标准库中的类型有std::pair and std::tuple,还有函数包装类型(std::function, std::packaged_type)和容器适配器(std::stack和朋友)。

作用域分配器允许调整外部分配器或外部分配器和一组内部分配器(如果它们不相同),从而通知作用域分配器感知容器要用于内部“使用分配器”类型的分配器类型。

来自 boost 文档scoped_allocator_adaptor:

该类是 C++03 兼容的实现std::scoped_allocator_adaptor。 类模板scoped_allocator_adaptor是一个分配器模板,指定 容器(与任何其他容器一样)使用的内存资源(外部分配器) allocator 执行)并且还指定要传递给的内部分配器资源 容器内每个元素的构造函数。

该适配器使用一个外部分配器和零个或多个内部分配器进行实例化 类型。如果仅使用一种分配器类型实例化,则内部分配器 成为scoped_allocator_adaptor本身,因此使用相同的分配器 容器和容器内每个元素的资源,如果 元素本身是容器,它们的每个元素都是递归的。

如果使用多个分配器实例化,则第一个分配器是 外部分配器供容器使用,第二个分配器被传递给 容器元素的构造函数,以及元素本身 是容器,第三个分配器被传递给元素的元素,并且 很快。如果容器嵌套的深度大于容器的数量 分配器,最后一个分配器被重复使用,就像在单一分配器中一样 对于任何剩余的递归。

根据我的经验,Boost Container 始终比大多数标准库实现更好地支持该协议,但我承认我已经很多年没有检查过这些协议的状态了。

我刚刚为您提供了一个使用示例scoped_allocator_adaptor在这个答案中:boost进程间是否支持在进程之间共享包含指针的对象? https://stackoverflow.com/questions/72310393/does-boost-interprocess-support-sharing-objects-containing-pointers-between-proc/72312694#72312694。在那张照片里,Shared::Bar是内部“uses-allocator”类型的一个示例,它通知容器(vector)在构造元素时传递其分配器的(转换后的)副本。

我在此处复制该答案的代码列表,以确保示例参考不会过时:

住在科里鲁 http://coliru.stacked-crooked.com/a/3dece79f14055b92

#include <boost/container/scoped_allocator.hpp>
#include <boost/container/string.hpp>
#include <boost/container/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <string>
namespace bip = boost::interprocess;

namespace Shared {
#ifdef COLIRU
    using Segment = bip::managed_mapped_file;
#else
    using Segment = bip::managed_shared_memory;
#endif
    using Mgr     = Segment::segment_manager;

    template <typename T>
    using Alloc = boost::container::scoped_allocator_adaptor< //
        bip::allocator<T, Mgr>>;

    template <typename T> using Vector = std::vector<T, Alloc<T>>;

    using String = boost::container::basic_string< //
        char, std::char_traits<char>, Alloc<char>>;

    struct Bar {
        using allocator_type = Alloc<char>;
        String first_name, last_name;

        template <typename Alloc>
        Bar(std::allocator_arg_t, Alloc alloc) : first_name(alloc)
                                               , last_name(alloc) {}

        template <typename Alloc>
        Bar(Alloc&& alloc) : first_name(alloc)
                           , last_name(alloc) {}

        Bar(Bar const&) = default;
        template <typename Alloc>
        Bar(Bar const& rhs, Alloc alloc) : first_name(rhs.first_name, alloc), last_name(rhs.last_name, alloc) {}

        Bar& operator=(Bar const&) = default;

        template <typename Alloc> Bar(std::string_view first_name, std::string_view last_name, Alloc alloc) : 
            first_name(first_name, alloc), last_name(last_name, alloc) {}
    };

    struct Snafu {
        std::array<int, 5> no_problem;
    };

    struct Foo {
        Vector<Bar>   bars;
        Vector<Snafu> snafus;

        template <typename Alloc> //
        Foo(Alloc alloc) : bars(alloc)
                         , snafus(alloc) {}
    };
}

#include <iostream>
static inline std::ostream& operator<<(std::ostream& os, Shared::Foo const& foo) {
    os << "Foo\n================\nBars:\n";
    for (auto& [f, l] : foo.bars)
        os << " - " << f << ", " << l << "\n";

    os << "Snafus:";
    for (auto& s : foo.snafus) {
        os << "\n - ";
        for (auto el : s.no_problem)
            os << " " << el;
    }
    return os << "\n";
}


int main() { Shared::Segment msm(bip::open_or_create, "my_shared_mem", 10ull << 10);
    Shared::Foo& foo = *msm.find_or_construct<Shared::Foo>("the_foo") //
                        (msm.get_segment_manager()); // constructor arguments

    foo.bars.emplace_back("John", "Doe");
    foo.bars.emplace_back("Jane", "Deer");
    foo.bars.emplace_back("Igor", "Stravinsky");
    foo.bars.emplace_back("Rimsky", "Korsakov");

    foo.snafus.push_back({1, 2, 3});
    foo.snafus.push_back({2, 3, 4});
    foo.snafus.push_back({3, 4, 5, 6, 7});

    std::cout << foo << "\n";
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中的“使用分配器”和“作用域分配器”构造是什么 的相关文章

随机推荐