I'm having trouble understanding what the difference is between using span<T> to create a view over some memory area/buffer as a struct of type T, or using placement-new to initialize a pointer type.
Functionally, they both allow you to write something like foos[0], and to read/write to the underlying memory area.
If I understand it correctly, the placement-new starts the "lifetime" of the pointers in the memory area/buffer -- but functionally, I don't notice any difference in effects between the two.
Below shows an example of what I mean:
//              BUFFER
// |==================================|
// | Page | Page | Page | Page | Page |
// |==================================|
// ^                                  ^
// 0 bytes                            (4096 * 5) bytes
#include <cstddef>
#include <cstdio>
#include <span>
#include <cassert>
struct alignas(4096) Page { int id; };
alignas(4096) std::byte buffer[4096 * 5];
// Using placement-new to initialize Page pointers in the buffer
Page* page_ptrs = new (buffer) Page;
// Using span<T> to create a view of Pages over the buffer
auto pages = std::span<Page>(reinterpret_cast<Page*>(buffer), 5);
int main()
{
    // Get first and second page using page_ptrs
    Page* first_page = &page_ptrs[0];
    Page* second_page = &page_ptrs[1];
    first_page->id = 1;
    second_page->id = 2;
    // Get first and second page using pages
    Page first_page_2 = pages[0];
    Page second_page_2 = pages[1];
    printf("first_page.id = %d, second_page.id = %d \n", first_page->id, second_page->id);
    printf("first_page_2.id = %d, second_page_2.id = %d \n", first_page_2.id, second_page_2.id);
    assert(first_page->id == first_page_2.id);
    assert(second_page->id == second_page_2.id);
    // Update the page ID's using the span<T>
    pages[0].id = 3;
    pages[1].id = 4;
    assert(first_page->id == 3);
    assert(second_page->id == 4);
}
