8

Should there be a standard C++ pattern for this? transform_to

 4 years ago
source link: https://devblogs.microsoft.com/oldnewthing/20200228-00/?p=103498
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
quQ73i7.jpg!web

Raymond

February 28th, 2020

I’ve got one type of collection and I want to apply a function to each member of the collection, thereby producing a new collection.

Surely there’s a standard pattern for this?

In JavaScript, it’s called map :

function getOldValues()
{
    return ["a", "b", "c", "d"];
}

var newValues = getOldValues().map(v => v.charCodeAt(0));
// result: [97, 98, 99, 100]

In C#, it’s Select .

string[] GetOldValues() => new[] { "a", "b", "c", "d" };

var newValues = GetOldValues().Select(v => (int)v[0]).ToArray();
// result: int[] { 97, 98, 99, 100 };

In C++, it’s, um, this clumsy std::transform .

std::vector<std::string> GetOldValues()
{
   return { "a", "b", "c", "d" };
}

auto oldValues = GetOldValues();
auto newValues = std::vector<int>(oldValues.size());
std::transform(oldValues.begin(), oldValues.end(),
    std::back_inserter(newValues),
    [](auto&& v) { return v[0]; });

It’s clumsy because you need to give a name to the thing being transformed, because you need to call both begin and end on it. But giving it a name extends its lifetime, so you end up carrying this oldValues vector around for no reason.¹

It’s clumsy because you have to construct an empty newValues and then fill it in.

Would be nice if there were some helper function like

template<typename T, typename U, typename TLambda>
T transform_to(U&& u, TLambda&& lambda)
{
  T result;
  if constexpr (has_size_v<U> && has_reserve_v<T>)
  {
    result.reserve(u.size());
  }
  std::transform(u.begin(), u.end(), std::back_inserter(result),
                 std::forward<TLambda>(lambda));
  return result;
}

auto newValues = std::transform_to<std::vector<int>>(
    GetOldValues(), [](auto&& v) { return v[0]; });

Maybe one exists and I’m missing it? Help me out here.

¹ You can avoid extending the lifetime beyond the transform by pushing it into a lambda:

auto newValues = [&]()
{
    auto oldValues = GetOldValues();
    auto newValues = std::vector<int>(oldValues.size());
    std::transform(oldValues.begin(), oldValues.end(),
        std::back_inserter(newValues),
        [](auto&& v) { return v[0]; });
    return newValues;
}();

but that’s basically just taking the transform_to function and inlining it as a lambda.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK