问题
STL defines two flavors of the transform function
The first is For unary operators:
template <class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform (InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperation op);
And the second is for binary operators:
template <class InputIterator1, class InputIterator2,
class OutputIterator, class BinaryOperation>
OutputIterator transform (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, OutputIterator result,
BinaryOperation binary_op);
What is the most efficient implementation of a similiar function for a ternary operator?
EDIT: Here is the trivial implementation I came up with, but isn't there a leaner and more elegant solution?
template <class InputIterator1, class InputIterator2, class InputIterator3,
class OutputIterator, class TrenaryOperation>
OutputIterator transform3(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator3 first3, OutputIterator result,
TrenaryOperation trenary_op)
{
while (first1 != last1) {
*result = trenary_op(*first1, *first2, *first3);
++result; ++first1; ++first2; ++first3;
}
return result;
}
回答1:
A simple version of this can be achieved to create an n-ary transform like this:
template <class Functor, class OutputIterator,
class Input1, class ... Inputs>
OutputIterator transform(Functor f, OutputIterator out,
Input1 first1, Input1 last1,
Inputs ... inputs)
{
while(first1 != last1)
*out++ = f(*first1++, *inputs++...);
return out;
}
This version tries to stay as close to the existing transform
as possible, taking one first
/last
pair of iterators and the rest are just first
s. This leaves it up to the user to make sure all the ranges are valid, just as with the binary-transform.
As for performance, I agree with ShighShagh's comment about performance not likely being an issue here. The compiler will be in a better place than you to determine what optimizations to take because each instantiation could lead to different situations that the programmer couldn't possibly know while writing this function.
回答2:
Here is my take at it. I got a little carried away, which resulted in a transform function for N dimensions:
#include <iostream> // for std::cout
#include <iterator> // for std::ostream_iterator
#include <tuple> // for std::tie
#include <type_traits> // for std::enable_if
#include <vector> // for std::vector
template<typename T>
struct identity { using type = T; };
template<typename Integral, Integral... N>
struct integer_sequence {
template<Integral Offset>
struct offset : identity<integer_sequence<Integral, (N + Offset)...>> { };
};
namespace detail {
template<typename... T>
void ignore(T&&...) { }
template<std::size_t Idx, typename... T>
inline auto nth_arg(T&&... arg)
-> decltype(std::get<Idx>(std::tie(arg...))) {
return std::get<Idx>(std::tie(arg...));
}
template<std::size_t N, std::size_t... T>
struct gen_iter_indices
: gen_iter_indices<(N - 2), (N - 2), T...> { };
template<std::size_t... T>
struct gen_iter_indices<0, T...>
: identity<integer_sequence<std::size_t, T...>> { };
template<
typename... Iterator,
typename Integral,
Integral... Begin,
Integral... End
>
inline static bool eq_n(const std::tuple<Iterator...>& iters,
integer_sequence<Integral, Begin...>,
integer_sequence<Integral, End...>)
{
const bool res[] { (std::get<Begin>(iters) == std::get<End>(iters))... };
for(std::size_t i = 0; i < sizeof...(Begin); ++i) {
if(res[i]) { return true; }
}
return false;
}
template<typename... Iterator, typename Integral, Integral... Begin>
inline static void increment_n(const std::tuple<Iterator...>& iters,
integer_sequence<Integral, Begin...>)
{
ignore(++std::get<Begin>(iters)...);
}
template<
typename NaryOperation,
typename... Iterator,
typename Integral,
Integral... Begin
>
inline auto call_n(const std::tuple<Iterator...>& iters,
NaryOperation op,
integer_sequence<Integral, Begin...>)
-> decltype(op(*std::get<Begin>(iters)...))
{
return op(*std::get<Begin>(iters)...);
}
}
template<
typename OutputIter,
typename NaryOperation,
typename... InputIter,
typename = typename std::enable_if<
(2 <= sizeof...(InputIter)) && // Atleast one iterator pair
(0 == (sizeof...(InputIter) % 2)) // and multiple of two
>::type
>
static OutputIter transform_n(OutputIter out_iter,
NaryOperation op,
InputIter... in_iter)
{
using begins = typename detail::gen_iter_indices<sizeof...(InputIter)>::type;
using ends = typename begins::template offset<1>::type;
const auto iters = std::tie(in_iter...); // tuple of references to iterators
while(!detail::eq_n(iters, begins{}, ends{})) {
*out_iter = detail::call_n(iters, op, begins{});
++out_iter;
detail::increment_n(iters, begins{});
}
return out_iter;
}
Usage is simple:
int main(int argc, char** argv) {
std::vector<int> v1 { 1, 2, 3 };
std::vector<int> v2 { 4, 5, 6 };
std::vector<int> v3 { 7, 8, 9 };
std::vector<int> res { };
res.resize(3);
auto end = transform_n(
res.begin(),
[](int _1, int _2, int _3) { return _1 + _2 + _3; },
v1.begin(),
v1.end(),
v2.begin(),
v2.end(),
v3.begin(),
v3.end()
);
std::copy(res.begin(), end, std::ostream_iterator<int>(std::cout, " "));
return 0;
}
Output on ideone.
Note that in this version works with containers or different sizes, so if you know your containers will always be the same size, you can edit detail::eq_n
to only check the first begin/end iterators for equality.
来源:https://stackoverflow.com/questions/21758718/best-stl-transform-like-template-function-for-ternary-operators