Итак, вы хотите иметь возможность умножать функции.Ну, это звучит хорошо.Почему бы не +
и -
и /
, пока мы там?
template<class F>
struct alg_fun;
template<class F>
alg_fun<F> make_alg_fun( F f );
template<class F>
struct alg_fun:F {
alg_fun(F f):F(std::move(f)){}
alg_fun(alg_fun const&)=default;
alg_fun(alg_fun &&)=default;
alg_fun& operator=(alg_fun const&)=default;
alg_fun& operator=(alg_fun &&)=default;
template<class G, class Op>
friend auto bin_op( alg_fun<F> f, alg_fun<G> g, Op op ) {
return make_alg_fun(
[f=std::move(f), g=std::move(g), op=std::move(op)](auto&&...args){
return op( f(decltype(args)(args)...), g(decltype(args)(args)...) );
}
);
}
template<class G>
friend auto operator+( alg_fun<F> f, alg_fun<G> g ) {
return bin_op( std::move(f), std::move(g), std::plus<>{} );
}
template<class G>
friend auto operator-( alg_fun<F> f, alg_fun<G> g ) {
return bin_op( std::move(f), std::move(g), std::minus<>{} );
}
template<class G>
friend auto operator*( alg_fun<F> f, alg_fun<G> g ) {
return bin_op( std::move(f), std::move(g),
std::multiplies<>{} );
}
template<class G>
friend auto operator/( alg_fun<F> f, alg_fun<G> g ) {
return bin_op( std::move(f), std::move(g),
std::divides<>{} );
}
template<class Rhs,
std::enable_if_t< std::is_convertible<alg_fun<Rhs>, F>{}, bool> = true
>
alg_fun( alg_fun<Rhs> rhs ):
F(std::move(rhs))
{}
// often doesn't compile:
template<class G>
alg_fun& operator-=( alg_fun<G> rhs )& {
*this = std::move(*this)-std::move(rhs);
return *this;
}
template<class G>
alg_fun& operator+=( alg_fun<G> rhs )& {
*this = std::move(*this)+std::move(rhs);
return *this;
}
template<class G>
alg_fun& operator*=( alg_fun<G> rhs )& {
*this = std::move(*this)*std::move(rhs);
return *this;
}
template<class G>
alg_fun& operator/=( alg_fun<G> rhs )& {
*this = std::move(*this)/std::move(rhs);
return *this;
}
};
template<class F>
alg_fun<F> make_alg_fun( F f ) { return {std::move(f)}; }
auto identity = make_alg_fun([](auto&& x){ return decltype(x)(x); });
template<class X>
auto always_return( X&& x ) {
return make_alg_fun([x=std::forward<X>(x)](auto&&... /* ignored */) {
return x;
});
}
Я думаю, что по этому поводу.
auto square = identity*identity;
у нас также может быть тип-стерты alg funs.
template<class Out, class...In>
using alg_map = alg_fun< std::function<Out(In...)> >;
это те, которые поддерживают такие вещи, как *=
.alg_fun
обычно не набирайте erase достаточно, чтобы.
template<class Out, class... In>
alg_map<Out, In...> pow( alg_map<Out, In...> f, std::size_t n ) {
if (n==0) return always_return(Out(1));
auto r = f;
for (std::size_t i = 1; i < n; ++i) {
r *= f;
}
return r;
}
можно было бы сделать более эффективно.
Тестовый код:
auto add_3 = make_alg_fun( [](auto&& x){ return x+3; } );
std::cout << (square * add_3)(3) << "\n";; // Prints 54, aka 3*3 * (3+3)
alg_map<int, int> f = identity;
std::cout << pow(f, 10)(2) << "\n"; // prints 1024
Liveпример .
Вот более эффективный режим, который работает без стирания типа:
inline auto raise(std::size_t n) {
return make_alg_fun([n](auto&&x)
-> std::decay_t<decltype(x)>
{
std::decay_t<decltype(x)> r = 1;
auto tmp = decltype(x)(x);
std::size_t bit = 0;
auto mask = n;
while(mask) {
if ( mask & (1<<bit))
r *= tmp;
mask = mask & ~(1<<bit);
tmp *= tmp;
++bit;
}
return r;
});
}
template<class F>
auto pow( alg_fun<F> f, std::size_t n ) {
return compose( raise(n), std::move(f) );
}
Живой пример .Он использует новую функцию compose
в alg_fun
:
template<class G>
friend auto compose( alg_fun lhs, alg_fun<G> rhs ) {
return make_alg_fun( [lhs=std::move(lhs), rhs=std::move(rhs)](auto&&...args){
return lhs(rhs(decltype(args)(args)...));
});
}
, которая выполняет compose(f,g)(x) := f(g(x))
Ваш код теперь буквально становится
alg_fun<Helper> h;
alg_fun<F> f;
auto result = pow( h, 10 )*f;
, что h(x)*h(x)*h(x)*h(x)*h(x)*h(x)*h(x)*h(x)*h(x)*h(x)*f(x)
.За исключением (с эффективной версией), я только один раз вызываю h
и просто поднимаю результат до степени 10.