Exceptional engineers I've come across unilaterally don't use smart/unique/whatever_ptr. A common theme is that they employ allocation strategies that avoid the need for 'micro' memory management in favor of 'macro' management strategies. Allocating large blocks of memory at a time and freeing it all at once is one specific example.
Having worked on a few decently large C++ codebases, I can confidently say that this is not how I view things. Shared pointers might be pretty drastically overused by some programmers but have use cases, and I think unique pointers are pretty invaluable.
I can't imagine writing a long running, memory conscious, and fast C++ program that uses whatever 'macro' management strategy you envision.
This is pretty simple actually! At startup, create some object pools, arenas, or bump allocators. Then, never heap allocate anything ever.
We also happen to do 0 other steady-state syscalls (not just 0 mmap, munmap, mremap, etc.), so we can just run the program under valgrind and any time valgrind prints something is a bug.
We didn't use C++ but some other software I've heard uses a similar approach is written in C++.
Arena allocation and smart pointer tackle fairly different problems. Not sure why you're conflating these different problems? I'm extensively using both of them on a fairly large code base (>10M) daily basis. Without smart pointers, I'm confident that engineers will need to spend 2x more time on figuring out actual ownership of pointers.
If you're working on a nice code base that has a very clean boundary across teams and infrastructures, have the arena boundary follow that principle and the system has relatively straightforward lifetime and ownership, then yes. Obviously this is not true for so-called "large scale software systems". Have you tried to bounce objects across arenas thanks to all the teams that wanted to "optimize" and "simplify" their memory allocation? Good luck with debugging that.
The point of arenas is usually to not care about ownership for object graphs that are allocated in the same arena, and to avoid immediate destruction for said objects.
The typical pattern is to create an arena whose lifetime is the lifetime of some important operation in your program (say, serving a single request or processing a single frame), do all of the logic of that operation via simple allocation in the arena (ideally bump-pointer allocation), don't free/delete anything at all, then when the operation is finished, just free the arena itself (or don't, and reuse it for the next operation by just resetting the pointer).
This implies, or at least allows, a very different style of writing C++ than usual - no need for tracking ownership, so no RAII, so no constructors or destructors, so no smart pointers.
Of course, you can also write this kind of code with RAII and smart pointers (with nop destructors for non-resource objects), using placement new to construct objects inside the arena and so on. But it's not necessary, and some find it easier to avoid it.
I think arenas are more of an alternative to shared_ptr than to unique_ptr.
A std::string is an example we can use, it acts fairly similar to unique_ptr. Sometimes you just need to heap allocate something, work with it and/or store it as a member, maybe pass it to a function or whatever, and then have it cleaned up at the end of the scope or when the object that it's a member of is destroyed. I don't think an arena can replace this use case.
If we need something with multiple handles, that doesn't have a trivial and predictable lifetime (such as objects / variables in a programming language interpreter, or entities in a video game), you can reach for shared_ptr or an arena. In the specific example I gave I would certainly prefer the arena, and would implement a garbage collector to deallocate stuff when it became unreachable.
The arena pattern is fairly common in Rust, because in Rust even a reference counted smart pointer doesn't let you bypass the "shared ^ mutable" enforcement from the borrow checker! Having everything owned by the arena greatly simplifies issues with the borrowck, and you can just allow callers to take temporary references and drop them when they are finished with them. There is a crate called "SlotMap" that provides a data structure to assist with the pattern (there is a C++ SlotMap library as well somewhere).
Anyways I have rambled a bit, but I think unique_ptr solves a different problem than what you describe, which instead is more of an alternative to reference counted pointers (shared_ptr).
Most typically when using arenas, you don't want to pay the cost of de-allocation at all while the arena is still alive. So, you actually have to "fight" unique_ptr, since you don't want it to call any kind of delete() or destructor, and you essentially get nothing in return.
If you use a bare pointer instead, not only do you get the same guarantees, but now if you need to modify the code to actually share that object, there's no need to modify any references from unique_ptr to shared_ptr.
Yeah! That's what I meant when I said arenas solve a different problem! I don't think I mentioned using unique_ptr with the arena and I didn't mean to suggest that if I did. My point was that arenas are not a replacement for unique_ptr at all, and instead solve a different problem where allocations don't have a simple and deterministic lifetime.
With an arena ideally you can just pass out actual references (T&) to callers instead of pointers!
I believe that there are certain situations where one may choose between scope-based memory management and arena-based. Even for the example of the rendering code of a frame, instead of allocating inside an arena, you could choose to allocate an object graph owned by some frame-level object, that is de-allocated once the frame is rendered.