30 Nov 2023 • C++ tricks: STL-free initializer list

Credit for this one to https://nitter.net/Donzanoid/status/1611315409596071936.

C++'s initializer_list is dogshit:

> cat a.cpp
#include <initializer_list>
> cl.exe /std:c++20 a.cpp /P > /dev/null; and wc -l a.i
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32534 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

8988 a.i
> g++ -E a.cpp -std=c++20 | wc -l
> clang++ -E a.cpp -std=c++20 | wc -l

There's actually no compiler magic involved here and initializer_list is pretty much just span so very simple to implement, but you need a few tricks to get it to actually work:

On that last point you may be wondering why bother with this at all. You can't keep a codebase completely STL-free, be it because of certain things where the non-STL way is extremely painful (e.g. MSVC atomics), or unavoidable third party libraries pulling in STL headers (e.g. metal-cpp). So we need to make this not clash with the real thing.


// GCC refuses to compile if the implementation doesn't exactly match the STL
#include <initializer_list>



namespace std {
    template< typename T >
    class initializer_list {

        constexpr initializer_list( const T * first_, const T * one_after_end_ ) : first( first_ ), one_after_end( one_after_end_ ) { }
        constexpr initializer_list( const T * first_, size_t n ) : first( first_ ), one_after_end( first_ + n ) { }

        const T * begin() const { return first; }
        const T * end() const { return one_after_end; }
        size_t size() const { return one_after_end - first; }

        const T * first;
        const T * one_after_end;


You may also want to add your own header guards against the STL defines, or you can just make sure you always include this first.