Short answer: read Sensible Error Handling 1, Sensible Error Handling 2, and Sensible Error Handling 3 by Niklas Frykholm. Actually, read all the articles on that blog, while you're at it. Won't say I agree with everything, but most of it is gold.
Don't use exceptions. There are a plethora of reasons. I will list the major ones.
They can indeed be slower, though that is minimized quite a bit on newer compilers. Some compilers support "zero overhead exceptions" for code paths that don't actually trigger exception (although that's a bit of a lie, since there's still extra data that exception handling requires, bloating your executable/dll size). The end result though is that yes, using exceptions is slower, and in any performance critical code paths, you should absolutely avoid them. Depending on your compiler, having them enabled at all may add overhead. They always always bloat code size, rather significantly in many cases, which can severely affect performance on today's hardware.
Exceptions make code much more fragile. There's an infamous graphic (which I sadly can't find right now) that basically just shows on a chart the difficulty of writing exception-safe code vs exception-unsafe code, and the former is a significantly larger bar. There are simply a lot of little gotchas with exceptions, and many many ways to write code that looks exception safe but really isn't. Even the entire C++11 committee goofed on this one and forgot to add an important helper functions for using std::unique_ptr correctly, and even with those helper functions, it takes more typing to use them than not to and most programmers won't even realize what's wrong if they don't.
More specifically for the games industry, some consoles' vendor-supplied compilers/runtimes outright don't support exceptions fully, or even don't support them at all. If your code uses exceptions, may you still in this day and age have to rewrite portions of your code to port it to new platforms. (Unsure if that's changed in the 7 years since said consoles were released; we just don't use exceptions, have them disabled in the compiler settings even, so I don't know that anyone I talk to has even checked recently.)
The general line of thinking is pretty clear: use exceptions for exceptional circumstances. Use them when your program gets into a "I have no idea what to do, maybe hopefully someone else does, so I'll throw an exception and see what happens." Use them when no other option makes sense. Use them when you don't care if you accidentally leak a little memory or fail to clean up a resource because you messed up the use of proper smart handles. In all other cases, don't use them.
Regarding code like your example, you have several other ways to fix the problem. One of the more robust -- though not necessarily most ideal in your simple example -- is to lean towards monadic error types. That is, createText() can return a custom handle type rather than an integer. This handle type has accessors for updating or controlling the text. If the handle is put into an error state (because createText() failed) then further calls to the handle simply fail silently. You can also query the handle to see if it has errored, and if so, what the last error was. This approach has more overhead than other options, but it's fairly solid. Use it in cases when you need to perform a long string of operations in some context where any single operation could fail in production, but where you don't/can't/won't check for errors after every call.
An alternative to implementing monadic error handling is to, rather than using custom handle objects, make the methods on the context object gracefully deal with invalid handle ids. For example, if createText() returns -1 when it fails, then any other calls to m_statistics that take one of those handles should gracefully exit if -1 is passed in.
You can likewise put the error printing inside the function that is actually failing. In your example, createText() likely has way more information about what went wrong, so it'll be able to dump a more meaningful error to the log. There's little benefit in this case to pushing the error handling/printing out to callers. Do that when the callers have need to customize the handling (or use dependency injection). Note that having an in-game console that can pop up whenever an error is logged is a good idea and helps here, too.
The best option (already presented in the linked series of articles above) for calls that you don't expect to fail in any sane environment -- like the simple act of creating text blobs for a statistics system -- is to just have the function that failed (createText in your example) abort. You can be reasonably sure that createText() is not going to fail in production unless something is totally wonked out (e.g., the user deleted font data files, or for some reason only has 256MB of memory, etc.). In many of these cases, there isn't even a good thing to do when failure does happen. Out of memory? You might not even be able to make an allocation necessary to create a nice GUI panel to show the user the OOM error. Missing fonts? Makes it hard to display errors to the user. Whatever's wrong, a crash handler can pop up a message to the user, offer to send the log file in to the developer, and hint that the user should probably reinstall the software since it's quite possibly missing some important component.
Just crashing is totally fine so long as you (a) log the error to a log file and (b) only do it on errors that aren't caused by regular user actions.
I wouldn't say the same thing at all for many server apps, where availability is critical and watchdog monitoring is not sufficient, but that is quite different than game client development. I would also strongly steer you clear of using C/C++ there as the exception handling facilities of other languages tend not to bite like C++'s since they are managed environments and don't have all the exception-safety problems that C++ has. Any performance concerns are also mitigated as servers tend to be focusing more on parallelism and throughput than minimum latency guarantees like the game clients. Even action game servers for shooters and the like can function quite well when written in C#, for example, since they're rarely pushing the hardware to its limits like FPS clients tend to do.