先看一段测试代码,

#include <iostream>
#include <list>
 
using namespace std;
 
struct A {int a; char b; long c; };
 
int main() {
        list<int> a{1,2,3};
        list<A> b;
        cout << "sizeof list<int>:" << sizeof(a) << endl;
        cout << "sizeof A:" << sizeof(A) << endl;
        cout << "sizeof list<A>:" << sizeof(b) << endl;
        return 0;
}

输出

sizeof list<int>:24
sizeof A:16
sizeof list<A>:24

WTF? 不符合直觉对吗?怎么着应该和模板参数有点儿关系才对?

要搞清楚为啥,我们看一下std::list(下称stl_list)定义

    /// Common part of a node in the %list.
    struct _List_node_base
    {
      _List_node_base* _M_next;
      _List_node_base* _M_prev;
	}
 
    /// The %list node header.
    struct _List_node_header : public _List_node_base
    {
#if _GLIBCXX_USE_CXX11_ABI
      std::size_t _M_size;
#endif 
	}
	
  /// An actual node in the %list.
  template<typename _Tp>
    struct _List_node : public __detail::_List_node_base
    {
#if __cplusplus >= 201103L
      __gnu_cxx::__aligned_membuf<_Tp> _M_storage;
      _Tp*       _M_valptr()       { return _M_storage._M_ptr(); }
      _Tp const* _M_valptr() const { return _M_storage._M_ptr(); }
#else
      _Tp _M_data;
      _Tp*       _M_valptr()       { return std::__addressof(_M_data); }
      _Tp const* _M_valptr() const { return std::__addressof(_M_data); }
#endif
    };

stl_list包含一个叫header的东西,在c++11之后,这个结构在64位系统上占24字节(两个指针各8字节,一个_M_size占8字节)。可以看到这个结构跟容器内元素的类型(模板参数)是无关的。同样,跟容器元素个数list::size()也无关。我们现在讨论的是std::list<int>这个类型的size,而非整个容器占用了多少内存空间。同理sizeof(vector<int>)也是一样,和模板参数类型无关,和vector::size()无关1

那我又要问了,这块和模板参数没关系,那容器中的数据到底咋存储的呢?

看上面的代码最后一个类_List_node,就是实际存储数据的节点类。同样继承_List_node_base,拥有双向指针和一个_M_storage。而这个类型的size就和模板参数相关了。

cout << "sizeof list_node<int>:" << sizeof(_List_node<int>) << endl; // 24 = 8(pointer) * 2 + 4(int) + 4(padding)
cout << "sizeof list_node<A>:" << sizeof(_List_node<A>) << endl; // 32 = 8(pointer) * 2 + 16(A)
cout << "sizeof list_header:" << sizeof(__detail::_List_node_header) << endl; // 24 = 8(pointer) * 2 + 8(size_t _M_size)

现在,一切都明朗了。并且,还可以知道list的结构实际上是这样的,

                                                                                         
            ┌─────────────────┐                                                          
            │_List_node_header│                                                          
            │                 │                                                          
empty list  │- prev = self    │                                                          
            │- next = self    │                                                          
            │- size = 0       │                                                          
            └─────────────────┘                                                          
                                                                                         
                                                                                         
                                                                                         
                     ┌──────────────────────────────────┐                                
                     ▼                                  │                                
            ┌─────────────────┐         ┌────────────┐  │                                
            │_List_node_header│         │_List_node  │  │                                
            │                 │         │            │  │                                
list{1}     │- prev ──────────┼────────►│* prev ─────┼──┘                                
            │- next ──────────┼────────►│* next ─────┼──┐                                
            │- size = 1       │         │* data = 1  │  │                                
            └─────────────────┘         └────────────┘  │                                
                     ▲                                  │                                
                     └──────────────────────────────────┘                                
                                                                                         
                                                                                         
                                                                                         
                                ┌────────────────────────────────────────────┐           
                                │                                            ▼           
            ┌─────────────────┐ │ ┌─────────────┐   ┌─────────────┐   ┌─────────────┐    
            │_List_node_header│ │ │_List_node   │   │_List_node   │   │_List_node   │    
            │                 │ │ │             │   │             │   │             │    
list{1,2,3} │- prev ──────────┼─┘ │* prev       ◄───┤* prev       ◄───│* prev       │    
            │- next ──────────┼──►│* next ──────┼──►│* next ──────┼──►│* next ──────┼─┐  
            │- size = 3       │   │* data = 1   │   │* data = 2   │   │* data = 3   │ │  
            └─────────────────┘   └─────────────┘   └─────────────┘   └─────────────┘ │  
                    ▲                                                                 │  
                    └─────────────────────────────────────────────────────────────────┘  

see also: https://cplusplus.com/forum/general/89868/

Footnotes

  1. 经历