【问题标题】:Why do I need std::get_temporary_buffer?为什么我需要 std::get_temporary_buffer?
【发布时间】:2011-03-16 21:52:13
【问题描述】:

我应该出于什么目的使用std::get_temporary_buffer?标准规定如下:

获得一个指向存储空间的指针,该存储空间足以存储多达 n 个相邻的 T 个对象。

我以为缓冲区会在堆栈上分配,但事实并非如此。根据 C++ 标准,这个缓冲区实际上不是临时的。这个函数比全局函数::operator new 有什么优势,全局函数::operator new 也不构造对象。我对以下陈述是等价的吗?

int* x;
x = std::get_temporary_buffer<int>( 10 ).first;
x = static_cast<int*>( ::operator new( 10*sizeof(int) ) );

这个函数是否只存在于语法糖?为什么名字里有temporary?


Dr. Dobb's Journal, July 01, 1996 中建议了一个用于实现算法的用例:

如果没有缓冲区可以分配,或者如果它比请求的小,算法仍然可以正常工作,只是变慢了。

【问题讨论】:

  • 仅供参考,std::get_temporary_buffer 将在 C++17 中被弃用。
  • @Deqing 是的。它也将在 C++20 中被删除,并且有充分的理由(如下所述)。所以沿着查看器移动..

标签: c++ memory-management stl buffer temporary


【解决方案1】:

Stroustrup 在“C++ 编程语言”(§19.4.4,SE)中说:

这个想法是,系统可能会保留一些固定大小的缓冲区以准备快速分配,以便为 n 个对象请求空间可能会产生超过 n 个的空间.然而,它也可能产生更少的收益,因此使用get_temporary_buffer() 的一种方法是乐观地要求很多,然后使用碰巧可用的东西。
[...] 因为get_temporary_buffer() 是低级的并且可能会针对管理临时缓冲区进行优化,所以它不应该用作 new 或 allocator::allocate() 的替代品 用于获得更长期的存储。

他还开始介绍这两个函数:

算法通常需要临时空间才能以可接受的方式执行。

...但似乎没有在任何地方提供 temporary 或 longer-term 的定义。

"From Mathematics to Generic Programming" 中的 anecdote 提到 Stepanov 在原始 STL 设计中提供了一个虚假的占位符实现,但是:

令他惊讶的是,多年后他发现所有提供 STL 实现的主要供应商仍在使用这种糟糕的实现 [...]

【讨论】:

  • 看起来 VC++ 对此的实现只是一个循环调用 operator new 并带有连续更小的参数,直到分配成功。那里没有特别的优化。
  • 与 g++ 4.5 相同 - 似乎是出于好意,但被供应商忽略了。
  • 听起来他们应该将此功能包装在一个名为crazy_allocator的类中
  • 但是让我们保持认真 - 也许有人会很乐意分配大量存储空间。我们现在可以做的是要求系统获得如此大量的存储空间——使用get_temporary_buffer。但是,如果我们收到的金额少于要求的数量(这将是一个真正的遗憾),我们会继续尝试使用我们拥有的存储空间来完成我们的工作。可能比捕获bad_alloc 由尝试分配比可用内存更多的内存引起的异常要好。然而,真正的用处取决于一个好的实现。
  • @jalf 在 C++17 中已弃用 :)(根据 cppreference.com)。
【解决方案2】:

Microsoft 的标准库人员说以下 (here):

  • 能否解释一下何时使用“get_temporary_buffer”

它有一个非常特殊的目的。请注意,它不会抛出 异常,例如 new (nothrow),但它也不构造对象, 不像新的(nothrow)。

STL 内部在 stable_partition() 等算法中使用它。 当有像 N3126 25.3.13 这样的魔术词时会发生这种情况 [alg.partitions]/11: stable_partition() 具有复杂性“最多(最后 - first) * log(last - first) 交换,但如果有足够的额外内存,则只有线性交换次数。”当魔术词“如果有 出现足够的额外内存”,STL 使用 get_temporary_buffer() 来 试图获得工作空间。如果可以,那么它可以实现 算法更高效。如果不能,因为系统正在运行 危险地接近内存不足(或涉及的范围很大), 该算法可以退回到较慢的技术。

99.9% 的 STL 用户永远不需要了解 get_temporary_buffer()。

【讨论】:

    【解决方案3】:

    标准规定它为最多 n 元素分配存储空间。 换句话说,您的示例可能会返回一个足够容纳 5 个对象的缓冲区。

    不过,似乎很难想象一个好的用例。也许如果您在一个内存非常有限的平台上工作,那么这是一种获得“尽可能多的内存”的便捷方式。

    但在这样一个受限的平台上,我想你会尽可能绕过内存分配器,并使用内存池或你可以完全控制的东西。

    【讨论】:

      【解决方案4】:

      出于什么目的我应该使用std::get_temporary_buffer?

      The function 在 C++17 中已弃用,因此现在正确答案是“无目的,请勿使用”。

      【讨论】:

        【解决方案5】:
        ptrdiff_t            request = 12
        pair<int*,ptrdiff_t> p       = get_temporary_buffer<int>(request);
        int*                 base    = p.first;
        ptrdiff_t            respond = p.sencond;
        assert( is_valid( base, base + respond ) );
        

        respond 可能小于 request。

        size_t require = 12;
        int*   base    = static_cast<int*>( ::operator new( require*sizeof(int) ) );
        assert( is_valid( base, base + require ) );
        

        base的实际大小必须大于或等于require。

        【讨论】:

          【解决方案6】:

          也许(只是猜测)它与内存碎片有关。如果您一直在大量分配和释放临时内存,但每次执行此操作时,您都会在分配临时内存之后但在释放临时内存之前分配一些长期预期内存,您最终可能会得到一个碎片堆(我猜)。

          因此,get_temporary_buffer 可能是一个比你需要的更大的内存块,它被分配一次(也许有很多块准备好接受多个请求),每次你需要内存时,你只需要块之一。所以内存不会碎片化。

          【讨论】:

          • 非常有趣的想法。尽管它目前似乎被大多数实现实现为 let-just-do-something-that-works,但这很可能得到更紧密的支持并与其余的内存管理例程集成。我实际上为我们投票期望这适合它,Bjarnes cmets 似乎也暗示了这一点。
          • 我现在已经查看了 Bjarne 的说法,他说它是为无需初始化的快速分配而设计的。所以它就像一个仅分配器(非初始化器)void* operator new(size_t size),但分配速度更快,因为它是预先分配的。
          猜你喜欢
          • 1970-01-01
          • 2016-03-14
          • 2021-10-16
          • 1970-01-01
          • 2016-09-07
          • 2012-07-28
          • 1970-01-01
          • 2019-08-22
          • 1970-01-01
          相关资源
          最近更新 更多