Матрицы массивов constexpr и функторы C ++ 17, совместимые с помощью операторов перегрузки - PullRequest
0 голосов
/ 22 февраля 2019

Я пытаюсь сделать два типа объектов совместимыми, используя контейнер, чтобы сделать его более сложным, мой код должен быть способен вычислять во время компиляции.

Один из моих типов объектов структурирован какФунктор следующим образом:

template <std::size_t n,std::size_t p=1,typename number=double>
class Oper{
    public:
    constexpr Oper(const number d_=1.):d(d_){}
    constexpr Oper(const Oper& oper_):d(oper_.d){}
    const number d ;
    constexpr auto operator()(const auto &v) const {
    typename std::remove_const<typename std::remove_reference<decltype(v)>::type>::type w{} ;
        /* Do some stuff to build w using v and d*/
    return w ;}
};

Другой тип объекта - просто std::array<std::array<double,n>,n>, то есть матрицы.

Эти объекты предоставляются в качестве свободных функций со следующими операторами:

template <std::size_t nr, std::size_t nc>
constexpr auto
operator *(const std::array<std::array<double,nc>,nr> & A,
           const std::array<double,nc> & x){
    std::array<double,nr> res{};
    for (auto i=0u;i<nr;i++)
        for (auto j=0u;j<nc;j++)
            res[i] += A[i][j]*x[j];
    return res;
}

template <std::size_t nrA, std::size_t ncA, std::size_t nrB, std::size_t ncB>
constexpr auto operator *(const std::array<std::array<double,ncA>,nrA> & A,
                          const std::array<std::array<double,ncB>,nrB> & B){
    std::array<std::array<double,ncB>,nrA> res{};
    for (auto k=0u;k<ncB;++k)
        for (auto i=0u;i<nrA;++i)
            for (auto j=0u;j<nrB;++j)
                res[i][k] += A[i][j]*B[j][k];
    return res ;
}

constexpr auto operator *(const auto & A,const auto & x){
    return A(x) ;
}

Имейте в виду, что я не могу изменить эти реализации, они предоставлены мне.

Чтобы сделать эти операторы совместимыми, я создал следующие классы

constexpr auto id = [](const auto v) {return v;};

template <typename Op, typename Preop=decltype(id)>
class Cont{
public:
    constexpr Cont(const Op & op_,const Preop & preop_=id):op(op_),preop(preop_){}
    constexpr Cont(const Cont & cont_):op(cont_.op),preop(cont_.preop){}
    const Op op ;
    const Preop preop ;
    template <typename Op2, typename Preop2>
    constexpr auto operator()(const Cont<Op2,Preop2> & mfop) const{
        return Cont<const Cont<Op,Preop>,const Cont<Op2,Preop2>>
               (Cont(op,preop),mfop);}
    constexpr auto operator()(const auto & v) const{return op*(preop*v);}
};

Такие, что следующие основныекомпилируется и работает как положено

int main(){
    const std::array<double,4> v{};
    const Cont A(Oper<4,1>(1.));
    const Cont B(std::array<std::array<double,4>,4>{std::array<double,4>{}});
    constexpr auto res = B*B*A*A*B*A*B*v;
    return 0;
}

Пример Godbolt здесь .

У меня проблемы с Cont классом

  1. Идея Cont при использовании только функторов (без массивов) состоит в том, чтобы каким-то образом размещать код, соответствующий разным функторам, один за другим и, в идеале, оптимизировать всю цепочку.Это может быть достигнуто?Как я должен это делать?Может быть, какая-то совершенная магия переадресации?

  2. В Cont делается много копий, это приводит к огромному использованию памяти компилятором (GCC 8.2.0 с флагами -std=c++1z -fconcepts -Ofast)при запросе полного времени компиляции.Я полагаю, что это из-за того, что все копии выполняются при соединении операторов.Это возможно?Есть ли способ избежать копирования?

...