It makes me wonder if this can be solved technically by published curated language subsets. Racket does this by supporting a bunch of different dialects, specifically to make it easier to teach [0].
One good do something similar for other languages. Step one is probably just writing a doc that says "Here's a standard subset of C++ we call Blah. These are the features it uses and these are the ones it doesn't: ..."
Then you could add tooling so that it will warn you if you use a prohibited feature.
Of course, this just pushes the problem up a level: now a new user has to know which curated sublanguage to use. But that's arguably simpler than doing it on a per-feature basis. At least they can just order a combo instead of having to pick a la carte.
I generally stick to it in all projects I write. The only exception is that in a few cases I'll use features that are disallowed in Google C++ primarily for historical reasons, most notably exceptions: https://google.github.io/styleguide/cppguide.html#Exceptions
The problem with exception-less C++ is that new, delete, ctors, and dtors become timebombs when they fail in a way that would have generated an exception.
I'm not sure why you were downvoted. I almost exclusively write exception-less C++ (compiling everything with "-fno-exceptions"), but correctly handling errors in constructors and destructors is absolutely an important concern. It's not hard to do, obviously, but it does require forethought and heavily encourages delegating complex logic to other parts of the code.
dtors that throw come with their own share of problems: If they do so while another exception is in flight (dtors are still called for objects when the stack is being unwound(?)), C++ crashes the process.
For our code base we avoid exceptions, except when due to awkard workarounds, or kludges mostly due to some API that's written in a way that you need to communicate with it with exceptions.
That to be said, even if you don't use exceptions in C++ - you must write exception safe code - e.g. as much as possible no manual "begin"/"end" but wrap things behind RAII, such that "end" is still called, in case of exception. Hence allocation through unique_ptr, shared_ptr, etc. is preferred over new/delete.
Because you never know the function you are calling whether deep down it won't throw an exception...
And some people choose to use more comprehensive documentation, like Google's C++ style guide, at the cost of imposing more restrictions that are largely in place for corporation-specific reasons that smaller personal codebases don't necessarily need to regard.
Choosing a curated language subset is exactly the "how do I curate the language" problem being referred to. The solution is not to subset a language, but to refuse to superset it. Stop building DSLs and frameworks and domain-specific abstractions and just stick to a flexible core.
It makes me wonder if this can be solved technically by published curated language subsets. Racket does this by supporting a bunch of different dialects, specifically to make it easier to teach [0].
One good do something similar for other languages. Step one is probably just writing a doc that says "Here's a standard subset of C++ we call Blah. These are the features it uses and these are the ones it doesn't: ..."
Then you could add tooling so that it will warn you if you use a prohibited feature.
Of course, this just pushes the problem up a level: now a new user has to know which curated sublanguage to use. But that's arguably simpler than doing it on a per-feature basis. At least they can just order a combo instead of having to pick a la carte.
[0]: https://docs.racket-lang.org/drracket/languages.html