On Thursday, January 12, 2017 at 7:20:04 PM UTC+1, Stefan Ram wrote:
> Wenn es die Möglichkeit der Überladung von Operatoren nicht
> gäbe, hätte sich vielleicht eine Kultur von Standardnamen
> für Funktionen herausgebildet, und man hätte sich darauf
> geeinigt, immer »increase« zu schreiben. Und zwar
> »increase( a, b )«, damit es auch für fundamentale Typen
> verwendbar ist.
>
> template< typename T >
> T & increase( T & a, T const & b )
> { a += b; return a; }
>
> Im Grunde haben wir so etwas heute ja mit Namen wie
> »begin« und »end«.
>
> sort( begin( str ), end( str ))
Dass sich "begin" und "end" eingebuergert haben, ist wohl in erster Linie Alexander Stepanov und seinen Mitarbeiterinnen zu verdanken, die mit der STL eine Fuelle generischer Algorithmen (wie eben "sort") mitgeliefert haben, die genau diese Konvention voraussetzen. Daher haben allen, die STL kompatibel sein wollen, diese Konvention gerne uebernommen.
Allerdings gaebe es durchaus auch Bedarf fuer Dein "increase".
Bei
std::vector<std::string> v = ...
scheitert
std::accumulate
schon bei der Verkettung von strings zu einem "super-string" mit einer Laenge von ein paar 10.000 Zeichen.
Da ware Bedarf nach einer Variante mit "+=", also z.B.
namespace hz
{
template<class InputIt, class T>
T accumulate_t(InputIt first, InputIt last, T init)
{
for (; first != last; ++first)
{
init += *first;
}
return init;
}
template<class InputIt, class T, class Trafo>
T accumulate_t(InputIt first, InputIt last, T init, Trafo t)
{
for (; first != last; ++first)
{
t(init,*first);
}
return init;
}
};
"Trafo" ist dann z.B. eine struct "increase:
template<class T, class U=T> struct increase
{
void operator()(T& obj, const U& arg)
{
obj += arg;
}
};
hz::accumulate_t schafft dann durchaus auch die
schon bei der Verkettung von strings zu einem "super-string" mit einer Laenge von ein paar 100 Millionen Zeichen.
Das fuehrt dann aber wieder zurueck zu Move Semantik und Operator-Overloading.
Eine Move-Variante von std::accumulate schafft das naemlich ebenfalls:
namespace hz
{
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first)
{
init = std::move(init) + *first;
}
return init;
}
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init, BinaryOperation op)
{
for (; first != last; ++first)
{
init = op(std::move(init),*first);
}
return init;
}
};
Als "BinaryOperation" muss dann eine Variante mit rvalue references genommen werden:
template<class T, class U=T> struct plus_rv: std::binary_function<T,U,T>
{
T operator()(T&& lhs, const U& rhs)
{
lhs += rhs;
return std::move(lhs);
}
};
Genauer Code mit Laufzeit-Ergebnissen im Anhang.
Helmut
======================= Code ==========================
#include <iostream>
#include <string>
#include <numeric>
#include <vector>
#include <chrono>
#include <ctime>
#include <cassert>
typedef std::vector<std::string> tVec;
struct timer
{
timer(const std::string& n): name(n), start{std::chrono::system_clock::now()}
{
}
~timer()
{
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();
std::cout << "Elapsed time for " << name << ": " << duration << "ms" << std::endl;
}
std::string name;
std::chrono::time_point<std::chrono::system_clock> start;
};
namespace hz
{
template<class InputIt, class T>
T accumulate_t(InputIt first, InputIt last, T init)
{
for (; first != last; ++first)
{
init += *first;
}
return init;
}
template<class InputIt, class T, class Trafo>
T accumulate_t(InputIt first, InputIt last, T init, Trafo t)
{
for (; first != last; ++first)
{
t(init,*first);
}
return init;
}
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first)
{
init = std::move(init) + *first;
}
return init;
}
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init, BinaryOperation op)
{
for (; first != last; ++first)
{
init = op(std::move(init),*first);
}
return init;
}
template<class T, class U=T> struct increase
{
void operator()(T& obj, const U& arg)
{
obj += arg;
}
};
template<class T, class U=T> struct plus_rv: std::binary_function<T,U,T>
{
T operator()(T&& lhs, const U& rhs)
{
lhs += rhs;
return std::move(lhs);
}
};
};
std::string std_acc(const tVec& in)
{
std::string init="";
timer t(__func__);
auto result = std::accumulate(in.begin(),in.end(), init);
std::cout << "result.size()=" << result.size() << std::endl;
return result;
}
namespace hz
{
std::string acc_t(const tVec& in)
{
std::string init="";
timer t(__func__);
auto result = hz::accumulate_t(in.begin(),in.end(), init);
std::cout << "result.size()=" << result.size() << std::endl;
return result;
}
std::string acc_t_t(const tVec& in)
{
std::string init="";
timer t(__func__);
auto result = hz::accumulate_t(in.begin(),in.end(), init, hz::increase<std::string>());
std::cout << "result.size()=" << result.size() << std::endl;
return result;
}
std::string acc(const tVec& in)
{
std::string init="";
timer t(__func__);
auto result = hz::accumulate(in.begin(),in.end(), init);
std::cout << "result.size()=" << result.size() << std::endl;
return result;
}
std::string acc_op(const tVec& in)
{
std::string init="";
timer t(__func__);
auto result = hz::accumulate(in.begin(),in.end(), init, hz::plus_rv<std::string>());
std::cout << "result.size()=" << result.size() << std::endl;
return result;
}
};
void test(const tVec& v)
{
static const int max_sz = 20*1000;
auto r = hz::acc_t(v);
assert(hz::acc_t_t(v)==r);
assert(hz::acc(v)==r);
assert(hz::acc_op(v)==r);
if(v.size() <= max_sz)
{
assert(std_acc(v)==r);
}
}
int main()
{
tVec v1, v2;
for(int i=0; i<20*1000; ++i)
{
v1.push_back("1234567890");
}
for(int i=0; i<100*1000*1000; ++i)
{
v2.push_back("1234567890");
}
test(v1);
test(v2);
};
================ Ergebnis =================
result.size()=200000
Elapsed time for acc_t: 0ms
result.size()=200000
Elapsed time for acc_t_t: 0ms
result.size()=200000
Elapsed time for acc: 0ms
result.size()=200000
Elapsed time for acc_op: 0ms
result.size()=200000
Elapsed time for std_acc: 2215ms
result.size()=1000000000
Elapsed time for acc_t: 2979ms
result.size()=1000000000
Elapsed time for acc_t_t: 2995ms
result.size()=1000000000
Elapsed time for acc: 3166ms
result.size()=1000000000
Elapsed time for acc_op: 3151ms