Simple std::regex_search() code won't compile with Apple clang++ -std=c++14

我们两清 提交于 2019-11-28 01:49:21

There was a change going from C++11 to C++14 where std::regex_search is no longer allowed to take a r-value

template< class STraits, class SAlloc,
          class Alloc, class CharT, class Traits >
bool regex_search( const std::basic_string<CharT,STraits,SAlloc>&&,
                   std::match_results<
                       typename std::basic_string<CharT,STraits,SAlloc>::const_iterator, 
                       Alloc>&,
                   const std::basic_regex<CharT, Traits>&,
                   std::regex_constants::match_flag_type flags =
                       std::regex_constants::match_default ) = delete;

This was added as the overload that takes a const std::string&

is prohibited from accepting temporary strings, otherwise this function populates match_results m with string iterators that become invalid immediately.

So you can no longer pass a temporary to std::regex_search as of C++14

To fix your code we would simply store the return from s() into a variable in main and use that to call std::regex_search.

#include <iostream>
#include <regex>

std::string s()
{
    return "test";
}

int main()
{
    static const std::regex regex(R"(\w)");
    std::smatch smatch;

    auto search = s();
    if (std::regex_search(search, smatch, regex)) {
        std::cout << smatch[0] << std::endl;
    }

    return 0;
}

Live Example

Shafik Yaghmour

This changed between C++11 and C++14. If we go to the cppreference section for std::regex_search we can see that overload that takes an rvalue reference was deleted since C++14:

template< class STraits, class SAlloc,
          class Alloc, class CharT, class Traits > bool regex_search( const std::basic_string<CharT,STraits,SAlloc>&&,
                   std::match_results<
                       typename std::basic_string<CharT,STraits,SAlloc>::const_iterator,
                       Alloc
                   >&,
                   const std::basic_regex<CharT, Traits>&,
                   std::regex_constants::match_flag_type flags =
                       std::regex_constants::match_default ) = delete;

It was changed due to LWG issue 2329: regex_match()/regex_search() with match_results should forbid temporary strings which says (emphasis mine):

Consider the following code:

const regex r(R"(meow(\d+)\.txt)");
smatch m;
if (regex_match(dir_iter->path().filename().string(), m, r)) {
  DoSomethingWith(m[1]);
}

This occasionally crashes. The problem is that dir_iter->path().filename().string() returns a temporary string, so the match_results contains invalidated iterators into a destroyed temporary string.

It's fine for regex_match/regex_search(str, reg) to accept temporary strings, because they just return bool. However, the overloads taking match_results should forbid temporary strings.

and indeed if we use a non-temporary:

std::string s1 = s() ;

if (std::regex_search(s1, smatch, regex)) {
//...
}

it compiles (see it live) and no longer exhibits undefined behavior.

Interesting to note that gcc/libstdc++ has this overload deleted in C++11 mode as well see it live. Since this is undefined behavior it seems like a good solution.

This issue also pops up in other areas of the library see Visual Studio regex_iterator Bug? which deals with the same issue but with regex_iterator/regex_token_iterator.

This not a bug, but expected behaviour. The reason is that s() returns a temporary string, regex_search makes use of regex_match and consequently if a temporary string was utilized match results would contain iterators to a string that no longer exists. This would have been undefined behaviour. Thus, the committee abolished this regex_search overload in C++14.

You can also confirm in the standard 28.4 Header synopsis [re.syn]:

template <class ST, class SA, class Allocator, class charT, class traits>
bool regex_search(const basic_string<charT, ST, SA>&&,
match_results<
typename basic_string<charT, ST, SA>::const_iterator,
Allocator>&,
const basic_regex<charT, traits>&,
regex_constants::match_flag_type =
regex_constants::match_default) = delete;

As you can see the overload that takes a rvalue to a basic_string is marked deleted.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!