- Mastering the C++17 STL
- Arthur O'Dwyer
- 447字
- 2021-07-08 10:20:23
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>.
- Boost程序庫完全開發指南:深入C++”準”標準庫(第5版)
- Learning ASP.NET Core 2.0
- 零基礎學Java(第4版)
- CouchDB and PHP Web Development Beginner’s Guide
- PHP+MySQL+Dreamweaver動態網站開發實例教程
- Android開發:從0到1 (清華開發者書庫)
- C語言程序設計實驗指導 (第2版)
- Instant Lucene.NET
- Solr Cookbook(Third Edition)
- Spring Boot+MVC實戰指南
- Windows Phone 8 Game Development
- Machine Learning for Developers
- 深度學習入門:基于Python的理論與實現
- Java從入門到精通(視頻實戰版)
- Lync Server Cookbook