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

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>.

主站蜘蛛池模板: 丰镇市| 石泉县| 陇西县| 修水县| 塔河县| 辽阳市| 宝山区| 龙陵县| 博罗县| 广水市| 杨浦区| 柳州市| 台前县| 三门县| 甘德县| 宜州市| 静安区| 镇康县| 抚州市| 阜平县| 濮阳市| 印江| 交口县| 宜章县| 温泉县| 东山县| 普格县| 哈密市| 怀来县| 东城区| 营口市| 进贤县| 即墨市| 华蓥市| 遵义市| 西畴县| 阿坝县| 北安市| 云安县| 临颍县| 盐山县|