Кажется, я нашел проблему и ее решение.
Описание функции matcher:
/**
* Creates a RegexMatcher that will match the given input against this pattern. The
* RegexMatcher can then be used to perform match, find or replace operations
* on the input. Note that a RegexPattern object must not be deleted while
* RegexMatchers created from it still exist and might possibly be used again.
*
* The matcher will retain a reference to the supplied input string, and all regexp
* pattern matching operations happen directly on this original string. It is
* critical that the string not be altered or deleted before use by the regular
* expression operations is complete.
*
* @param input The input string to which the regular expression will be applied.
* @param status A reference to a UErrorCode to receive any errors.
* @return A RegexMatcher object for this pattern and input.
*
* @stable ICU 2.4
*/
virtual RegexMatcher *matcher(const UnicodeString &input,
UErrorCode &status) const;
Здесь говорится о том, что input не копируется в объект matcher. Т.е. вызывающий код несет ответственность за валидность оригинального объекта input. RegexMatcher в конструкторе вызывает метод reset, описание которого говорит то же самое об оригинальном объекте:
/**
* Resets this matcher with a new input string. This allows instances of RegexMatcher
* to be reused, which is more efficient than creating a new RegexMatcher for
* each input string to be processed.
* @param input The new string on which subsequent pattern matches will operate.
* The matcher retains a reference to the callers string, and operates
* directly on that. Ownership of the string remains with the caller.
* Because no copy of the string is made, it is essential that the
* caller not delete the string until after regexp operations on it
* are done.
* Note that while a reset on the matcher with an input string that is then
* modified across/during matcher operations may be supported currently for UnicodeString,
* this was not originally intended behavior, and support for this is not guaranteed
* in upcoming versions of ICU.
* @return this RegexMatcher.
* @stable ICU 2.4
*/
virtual RegexMatcher &reset(const UnicodeString &input);
В моем коде создается временный объект icu::UnicodeString, который уничтожается, как только заканчивается его scope (функция CreateRegexMatcher). Соответственно после уничтожения временного объекта matcher указывает на невалидную область памяти. Таким образом, нужно гарантировать, что время жизни исходной строки будет не меньше времени жизни matcher'а, который будет ее использовать.
Это можно сделать, например, создавая и используя RegexMatcher в одной области:
std::shared_ptr< icu::RegexPattern > pattern = GetRegexPatternFromCache(rules.pattern);
icu::ErrorCode status;
// temporary object lives as long as matcher is used
std::unique_ptr< icu::RegexMatcher > matcher { pattern->matcher(icu::UnicodeString::fromUTF8(line), status) };
// auto matcher = GetMatcherFromPattern(pattern, line);
if (status.isFailure() || !matcher) {
std::cout << fmt::format("Failed to create regex matcher, status - {}\n", status.errorName());
return false;
}
if (!matcher->matches(status)) {
std::cout << fmt::format("{} does not match\n", line);
return false;
} else {
std::cout << fmt::format("{} matches\n", line);
return true;
}