【问题标题】:Standard container re-allocation multipliers across popular toolchains跨流行工具链的标准容器重新分配乘数
【发布时间】:2025-12-03 17:10:01
【问题描述】:

std::basic_stringstd::vector 等容器在内部容量耗尽时执行自动重新分配。该标准规定,在重新分配后,.capacity() >= .size()

主流工具链在执行重新分配时使用了哪些实际乘数?


更新

到目前为止,我有:

Dinkumware:1.5(随 MSVS 和可能的 ICC 一起提供)

GNU libstdc++:2(随 GCC 和可能的 ICC 一起提供)

RW/Apache stdcxx:1.618(又名 φ)

STL端口:2

【问题讨论】:

    标签: c++ implementation containers


    【解决方案1】:

    Dinkumware STL(随 Visual Studio 提供)使用 1.5 乘数,Gcc 使用 2。其余的我无法确定,但我认为这些是最常用的数字。 (IIRC,我读过一次 大多数 实现使用 2)

    作为旁注,您正确地将其称为乘数,因为标准要求增长(至少)是几何的。

    【讨论】:

    • 谢谢。您有机会为这些数字提供参考吗?
    • 顺便说一句,是 Meyers(我敢肯定,还有其他人),他说“大多数实现”使用 2。重复这似乎很流行,很多人似乎认为总是如此,甚至是标准的。
    • @Tomalak,我在标题中手动检查了 gcc。基本上它是new_size = size + max( 1, size() );。在视觉工作室,我在 Stephan T. Lavavej 最近的 Channel9 视频中听到了它。在 GCC 中,确定新大小的向量的实际函数是 _M_check_len( size_type, const char* ),在 stl_vector.h
    • @David:谢谢。将保持这个问题的开放性;想看看是否有人可以从其他工具链和版本中提出更多示例。 :)
    • 顺便说一句,从标题中获取数字并不难,只是需要进行一些检查并习惯于库中奇怪的标识符,但是像向量这样的容器的实现不应该太复杂而无法解释。 (我知道这不是小事,去过那里,做过)
    【解决方案2】:

    旧问题的新答案。

    基本原理:可以通过编程方式回答答案,并且使用在线编译器,相对容易。这是一个可以帮助您回答这个问题的程序:

    #include <climits>
    #include <cstddef>
    #include <cstdlib>
    #ifndef _MSC_VER
    #   include <cxxabi.h>
    #endif
    #include <iostream>
    #include <memory>
    #include <string>
    #include <typeinfo>
    #include <type_traits>
    #include <limits>
    #include <vector>
    #include <string>
    
    template <typename T>
    std::string
    type_name()
    {
        typedef typename std::remove_reference<T>::type TR;
        std::unique_ptr<char, void(*)(void*)> own
               (
    #ifndef _MSC_VER
                    abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                               nullptr, nullptr),
    #else
                    nullptr,
    #endif
                    std::free
               );
        std::string r = own != nullptr ? own.get() : typeid(TR).name();
        if (std::is_const<TR>::value)
            r += " const";
        if (std::is_volatile<TR>::value)
            r += " volatile";
        if (std::is_lvalue_reference<T>::value)
            r += "&";
        else if (std::is_rvalue_reference<T>::value)
            r += "&&";
        return r;
    }
    
    template <class C>
    void
    test()
    {
        C c;
        std::cout << type_name<C>() << ":\n";
        std::size_t c0 = c.capacity();
        std::cout << "    Initial capacity is " << c0 << '\n';
        c.resize(c0);
        for (int i = 0; i < 10; ++i)
        {
            c.push_back(typename C::value_type{});
            std::size_t c1 = c.capacity();
            if (c0 != 0)
            {
                float f = static_cast<float>(c1)/c0;
                std::cout << "    growth factor appears to be " << f << '\n';
            }
            c0 = c1;
            c.resize(c0);
        }
    }
    
    int
    main()
    {
        test<std::vector<int>>();
        test<std::string>();
    }
    

    大部分复杂性有点不必要,因为它只是为了让 type_name 正常工作。

    libstdc++:

    http://melpon.org/wandbox/permlink/njaIG2uiR2vlCLZz

    似乎对向量和字符串都回答了一个实心 2。

    VS:

    http://webcompiler.cloudapp.net

    对于向量和字符串都非常接近 1.5。

    libc++

    http://melpon.org/wandbox/permlink/mXshrLJHgNuvE1mD

    对于向量和字符串都非常接近 2。

    请注意,该程序还告诉您string 的短字符串缓冲区是什么:libstdc++ 和 VS 为 15,libc++ 为 22。

    【讨论】:

      最近更新 更多