You don't really need a generic system then. The regex part needs to be able to spit out a human readable error of why it's failed. You can have bool TryCompile(string) and string GetReasonWhyCompilationFailed(void), etc, etc...
Out of curiosity, should the code care about why the function failed? I have an integer return code that is returned, and if it isn't successful, it propagates the code upward, but with a boolean approach, it either failed, or it didn't.
Now, this approach has overhead. Every object that can fail must now have a dynamic string object, whether it fails or not.
You said above that the code doesn't care why, but it must print the reason why to the user. So you end up with a boolean success, and a message for the user.
If storing the error in the regex is a concern, then TryCompile can return the reason string instead of a bool if you like -- where a null error string indicates success.
Or it could return a pair{ bool success, string error };
Alternatively, you can copy the Windows API's solution. They allocate an error string per thread, not per object. Many functions fail with an error code that's basically equivalent to ERROR__SUCCESS_IS_FALSE. If you get a failure like this, then you call a global function to retrieve a human readable string of the most recent error to occur on the current thread.
Under this design, you'd have a global ThreadLocal<string> g_error, and TryCompile would write to g_error before returning false.
My thinking is that if the ultimate destination of the errors is always a human -- either a user-facing GUI, or an assertion/log message for a debugging programmer -- then what you really want is a very descriptive string, to which you can append specific information, such as line numbers, values of variables involved, etc, etc. An error code doesn't allow for that kind of extension.
Also, if the destination is a human, then at some point you need a mechanism to convert the error code into a string anyway. For the sake of loose-coupling, it seems bad for a regex module to return a code, which is then converted to a string by a completely different module (say, the GUI). This would mean that the GUI module has intimate knowledge about regexes, so that it can translate regex-specific errors to English... It seems cleaner to keep that translation local to the regex module.
However, this then complicates international localization -- you don't really want all your text hard-coded to English...
To get around this, you'd need your error objects to be quite fat, containing all the extended information (line numbers, values of bad variables, etc), and a string which acts as a key into a localization dictionary.
That ends up being something pretty complex, like:
Dict[string, string] locale = { "Err_regex_foo" -> "A regex was bad, see column %d" };
struct Error { string key; vector<Variant> data; }
function TryCompile(...)
...
return Error{ "Err_regex_foo", { 42 } }
string TranslateError( Error e, Dict locale )
format = locale[e.key]
return sprintf( format, e.data )