官术网_书友最值得收藏!

Complicated copying with std::transform

You might have noticed, way back when we presented the implementation of std::copy, that the value_type of the two iterator type parameters were not constrained to be the same. This is a feature, not a bug! It means that we can write code that relies on implicit conversions and it will just Do The Right Thing:

    std::vector<const char *> input = {"hello", "world"};
std::vector<std::string> output(2);

std::copy(input.begin(), input.end(), output.begin());

assert(output[0] == "hello");
assert(output[1] == "world");

Looks trivial, right? Look closely! Deep within our instantiation of std::copy is a call to the implicit constructor that converts const char * (the type of *input.begin()) to std::string (the type of *output.begin()). So for the umpteenth time, we're seeing an example of generic code that does surprisingly complicated operations simply by virtue of being given certain iterator types.

But sometimes you want to apply a complicated transformation function during the copying operation--something more complicated than implicit conversions can handle. The standard library has got you covered!

    template<class InIt, class OutIt, class Unary>
OutIt transform(InIt first1, InIt last1, OutIt destination, Unary op)
{
while (first1 != last1) {
*destination = op(*first1);
++first1;
++destination;
}
return destination;
}

void test()
{
std::vector<std::string> input = {"hello", "world"};
std::vector<std::string> output(2);

std::transform(
input.begin(),
input.end(),
output.begin(),
[](std::string s) {
// It works for transforming in-place, too!
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
return s;
}
);

assert(input[0] == "hello");
assert(output[0] == "HELLO");
}

Sometimes you even need to do a transformation using a function that takes two arguments. Again the library has you covered:

    template<class InIt1, class InIt2, class OutIt, class Binary>
OutIt transform(InIt1 first1, InIt1 last1, InIt2 first2, OutIt destination,
Binary op)
{
while (first1 != last1) {
*destination = op(*first1, *first2);
++first1;
++first2;
++destination;
}
return destination;
}

This version of std::transform might be humorously described as a one-and-two-halves-range algorithm!

(What about functions of three arguments? Four arguments? Unfortunately there's no fully variadic version of std::transform; variadic templates weren't introduced to C++ until C++11. You might try implementing a variadic version and see what kinds of problems you run into--they're surmountable but certainly not trivial.)

The existence of std::transform gives us yet a third way to move data elements from one place to another:

    std::vector<std::string> input = {"hello", "world"};
std::vector<std::string> output(2);

// Third approach: use std::transform
std::transform(
input.begin(),
input.end(),
output.begin(),
std::move<std::string&>
);

I certainly don't recommend this approach, though! The biggest and reddest of its red flags is that it contains explicit specialization of the std::move template. Whenever you see an explicit specialization--those angle brackets after the template's name--that's an almost sure sign of very subtle and fragile code. Advanced readers might enjoy figuring out how the compiler deduces which of the two std::moves I meant; remember, there's one in <utility> and one in <algorithm>.

主站蜘蛛池模板: 巫溪县| 沽源县| 思南县| 华容县| 宝应县| 济阳县| 保靖县| 山东| 浦城县| 安陆市| 县级市| 汉中市| 太仆寺旗| 四川省| 琼海市| 安宁市| 大姚县| 开阳县| 永德县| 汶川县| 双牌县| 商洛市| 南川市| 莱阳市| 永春县| 南京市| 射阳县| 额敏县| 新邵县| 天水市| 西丰县| 康保县| 郑州市| 新乐市| 邵东县| 三河市| 农安县| 利川市| 奇台县| 睢宁县| 井研县|