It used to be much more involved in the past. Of course you could hand write the code, but as it was originally I believe a preprocessor was the only way to sensibly write signal support, something that AFAIK stopped being true around 2003 but by then moc was entrenched.
It parses your C++ headers to generate helpful metadata used for runtime reflection and slot-and-signals. All the generated code is C++.
The latest versions of CMake support moc out of the box, so developers hardly notice the moc step.