I am a little lost on what your problem is. Provide a simple/minimal example for us such as-
https://godbolt.org/z/xqMaeK87oShowing examples of code will most likely be better than you trying to describe it all in words.
Just define it static inside a function.
You will also start to get guard_aquire/release code, which in main would be a one time thing and ultimately unnecessary there since no return from main, but harmless (but also extra generated code to stumble over when trying to figure things out).
Constructors of statically allocated object are called before main() is entered, but the order is not defined
For each source file, they are done in the order of creation, so for example from my first linked example-
//i2c on A9/A10
I2c i2c{ MCU::I2c1_A9A10 };
//STS35 using i2c
STS35 sts35{ i2c };
the i2c object constructor (global) will be run before the sts35 object constructor (global) is run. The order could change if the following is done, but this is your own doing-
extern I2c i2c;
//STS35 using i2c
STS35 sts35{ i2c };
//i2c on A9/A10
I2c i2c{ MCU::I2c1_A9A10 };
and the danger here is that sts35 is going to be used before i2c is constructed. But in this case since sts35 is not using i2c until one of its methods is explicitly called this is fine (it only needs the address of the I2c object, which is available before i2c is constructed). If you wanted sts35 to actually use i2c in the constructor you simply have to put the i2c creation before sts35, but in this case since this i2c uses irq's, I simply would not do it as I would not want any irq code running in the global constructors (I disable irq's in startup code so that no irq can run while doing startup init, so if I do something like setup a timer with irq that fires sooner than I expect, it will have to wait until startup init is done).
I don't find it that hard to think about what a constructor does, and what its dependencies may be. I also use header-only for classes so all my class objects are defined in the main source file where I can also control object creation order if/when needed. My first linked example has one source file, and all the rest is headers (in use, there is also a startup.cpp source file, not shown).
That's why an init() method, called explicitely, when you have to initialize hardware resources is the easiest way to control that.
Nothing wrong with that approach, but it kind of leaves a bit of the C++ advantage unused. Just a little bit of thought is required to make sure you are not getting into trouble. It is quite nice to be able to have a single object definition take care of everything and not have to split up initialization into separate pieces of code. The first linked example has led, sw, uart, i2c, sts35, systick all done with a single line each, all are setup in global constructors including the use of irq's for i2c. I cannot forget to do any more required init as its all done. I have no need to deal with setting up pins for uart/i2c as enough info is provided so it is handled in the constructor. If I want to change the i2c pins used or peripheral instance it is a matter of changing the single object definition line as needed and let the constructor deal with the details (which peripheral instance, which pins, which irq is used, etc.).