一般采用 indexibles/callables 的线性组合

Generically taking linear combinations of indexibles/callables

我正在尝试全局缩放 callable/indexible 个对象(抽象数学意义上的向量)并将它们加在一起。

也就是说,我正在尝试采用定义 operator[]operator() 的对象的线性组合。

例如,我希望能够做到这一点:

LinearCombination<std::function<double(double, double)>> A([](double x, double y){
    return 1+x+std::pow(x,2)+std::sin(y);
});
LinearCombination<std::function<double(double, double)>> B([](double x, double y){
    return 1-x+std::cos(y);
});
A*= 2.5;
A += B;
std::cout << A(1.0,2.0) << std::endl;

我的尝试

// ZERO ///////////////////////////////////////////////////////////////////////////////////////////

namespace hidden {

    // tag dispatching: from 

    template<int r>
    struct rank : rank<r - 1> {};

    template<>
    struct rank<0> {};

    template<typename T>
    auto zero(rank<2>) -> decltype(static_cast<T>(0)) {
        return static_cast<T>(0);
    }

    template<typename T>
    auto zero(rank<1>) -> decltype(T::zero()) {
        return T::zero();
    }

    template<typename T>
    auto zero(rank<0>)->std::enable_if_t<
        std::is_assignable<std::function<double(double,double)>, T>::value
        , std::function<double(double,double)>> {
        return []() {
            return 0.0;
        };
    }
}

template<typename T>
auto zero() { return hidden::zero<T>(hidden::rank<10>{}); }

// LINEAR COMBINATION ///////////////////////////////////////////////////////////////////////////////////////////

template<typename V, typename C = double>
struct LinearCombination {
    struct Term {
        C coeff;
        V vector;

        // if V(x...) is defined
        template<typename ...X>
        auto operator()(X&&... x) const -> std::remove_reference_t<decltype(std::declval<V>()(std::forward<X>(x)...))> {
            return vector(std::forward<X>(x)...) * coeff;
        }

        // if V[i] is defined
        template<typename I>
        auto operator[](I i) const -> std::remove_reference_t<decltype(std::declval<V>()[i])> {
            return vector[i] * coeff;
        }

    };
    std::vector<Term> terms;

    LinearCombination() {} // zero

    /*implicit*/ LinearCombination(V&& v) {
        terms.push_back({ static_cast<C>(1), std::move(v) });
    }

    /*implicit*/ LinearCombination(Term&& term) {
        terms.push_back(std::move(term));
    }

    LinearCombination<V, C>& operator+=(LinearCombination<V, C>&& other) {
        terms.reserve(terms.size() + other.terms.size());
        std::move(std::begin(other.terms), std::end(other.terms), std::back_inserter(terms));
        other.terms.clear();
        return *this;
    }

    LinearCombination<V, C>& operator*=(C multiplier) {
        for (auto& term : terms) {
            term.coeff *= multiplier;
        }
        return *this;
    }

    // if V(x...) is defined
    template<typename ...X>
    auto operator()(X&&... x) const
         -> std::remove_reference_t<decltype(std::declval<V>()(std::forward<X>(x)...))> {
        auto result = zeroVector()(std::forward<X>(x)...);  <--------------- *** BAD FUNCTION CALL ***
                                                                             *************************
        for (const auto& term : terms) {
            result += term(std::forward<X>(x)...);
        }
        return result;
    }

    // if V[i] is defined
    template<typename I>
    auto operator[](I i) const -> std::remove_reference_t<decltype(std::declval<V>()[i])> {
        auto result = zeroVector()[i];
        for (const auto& term : terms) {
            result += term[i];
        }
        return result;
    }

private:
    static const V& zeroVector() {
        static V z = zero<V>();
        return z;
    }
};

这对我来说编译得很好,但我在指示的行上遇到异常(错误的函数调用)。你能帮忙吗?

这个函数:

template<typename T>
auto zero(rank<2>) -> decltype(static_cast<T>(0));

赢得重载决议反对:

template<typename T>
auto zero(rank<0>)->std::enable_if_t<
    std::is_assignable<std::function<double(double,double)>, T>::value
    , std::function<double(double,double)>>;

这是因为 rank<2>rank<0> 更适合 rank<10>{},并且:

static_cast<std::function<double(double,double)>>(0)

是一个有效的表达式。

也就是说,std::function 具有以下构造函数:

function(std::nullptr_t) noexcept;

这使得它成为 0 论点的可行选择,并且 static_cast 确实考虑了构造函数。

你最终得到 std::function<double(double,double)> 初始化为 0 (empty),当你试图调用它时会导致异常。