Search code examples
c++objectlambdareturn-valuetransformation

Returning objects constructed in lambda in transform


The following function does something different than I want, which is to return the matches. If I call it on vector<string>{"a b", "cd ef"}, the output is

cd 
cd ef

instead of

a b
cd ef

Why?

#include <regex>
using namespace std;

void f(const vector<string>& v) {
  vector<smatch> P{};
  transform(begin(v), end(), back_inserter(P), [](auto s){
    auto m = *new smatch;
    regex_match(s, m, regex{"(\\S*) (\\S*) ?(.*)"});
    return m;
  });
  for (auto s: P) cout << s[0] << endl; // debug output
}

It's the same if I try without new: smatch m{};. Which I believe I shouldn't do, because then the smatch is allocated on the stack and invalidated when the lambda function returns. (I even tried smatch m;, but that should create an uninitialized variable. Oddly, it works with no runtime error, giving the same wrong result.) And the following doesn't even compile, giving an error I don't understand:

#include <regex>
using namespace std;

void f(const vector<string>& v) {
  vector<smatch> P{};
  transform(begin(v), end(), back_inserter(P), [](auto s){
    auto m = new smatch;
    regex_match(s, m, regex{"(\\S*) (\\S*) ?(.*)"});
    cout << (*m)[0] << endl;
    return m;
  });
  for (auto s: P) cout << (*s)[0] << endl;
}

Solution

  • For std::match_results:

    Because std::match_results holds std::sub_matches, each of which is a pair of iterators into the original character sequence that was matched, it's undefined behavior to examine std::match_results if the original character sequence was destroyed or iterators to it were invalidated for other reasons.

    The parameter s of the lambda is passed by-value, it'll be destroyed when get out of the lambda. You can change it to pass-by-reference:

    void f(const vector<string>& v) {
      vector<smatch> P;
      transform(begin(v), end(v), back_inserter(P), [](auto& s){
        //                                                 ^
        smatch m;
        regex_match(s, m, regex{"(\\S*) (\\S*) ?(.*)"});
        return m;
      });
      for (auto s: P) cout << s[0] << endl; // debug output
    }