Segmentation fault in std::transform

2.3k views Asked by At

I'm trying to transfer parsed out the file names from regex match to the list of filesystem::path objects.

I believe that matches are valid because for_each for the same iterators and print to console work perfectly. However, I'm getting a segmentation fault running this code. What am I doing wrong? Is there a mistake in my lambda?

        namespace fs = boost::filesystem;
        std::forward_list<fs::path> results;
        std::transform(std::sregex_iterator(file_data.begin(), file_data.end(), re),
                       std::sregex_iterator(), results.begin(),
                       [&](const std::smatch& m)->fs::path{
            return root / fs::path(m[1].str());
        });

GDB shows me this line as a place of error:

path& operator=(const path& p)
{
  m_pathname = p.m_pathname;
  return *this;
}

UPDATE: found the solution - use back_inserter(results) instead of results.begin(). However, why is that?

2

There are 2 answers

0
templatetypedef On BEST ANSWER

The std::transform algorithm's third parameter should be an iterator to the start of the range where the values should be written. Specifically, it works by overwriting the values in the range pointed at by the iterator with the transformed values. This means that there actually have to be values there to overwrite in the first place. In your case, you're writing to an empty forward_list, so there's nothing to write to, hence the crash.

To fix this, consider replacing the last argument with a back_inserter, which will automatically create the space that's needed as the values are produced:

std::transform(std::sregex_iterator(file_data.begin(), file_data.end(), re),
               std::sregex_iterator(),
               back_inserter(results), // <--- This is new
               [&](const std::smatch& m)->fs::path{
        return root / fs::path(m[1].str());
});

More generally, to the best of my knowledge, all of the algorithms in <algorithm> that write to output ranges will assume that there are values available to overwrite in that range. If that isn't the case, consider using a back_inserter or other type of insert iterator, which will automatically create the space that's needed for you.

Hope this helps!

0
Useless On

Your output iterator is a simple results.begin(), which is probably == results.end(). The clue here is that the failure comes when trying to assign the result.

You either need a back_inserter as you found, or to use some container with enough space already allocated (which can only work if you know how many items you're transforming in advance).

Specifically, consider the sample implementation of the first overload here.

The line

*d_first++ = op(*first1++);

requires the destination iterator already to be valid. If it's == end() as suggested, the whole operation is illegal.