In my case it's a little more complicated than a simple Lua userdata wrapping a C++ object. In some cases, the Lua userdata "owns" the C++ object, in some cases it doesn't. I don't use shared_ptr or anything like that to manage it. And I don't use a separate "owning" flag like some wrappers do. I have a few bit flags in the object itself, plus some other contextual information, that I use to make that determination when the userdata is finalized. Also it can change over time.
I'm happy with my own simple wrapper, I just want to get the details correct.