Как я могу получить доступ к типам в пакете параметров шаблона C ++? - PullRequest
0 голосов
/ 20 апреля 2020

Я хочу построить класс, представляющий сопоставление значения типа I со значением типа O. Я создал этот интерфейс:

template <typename I, typename O> class Transform {
public:
    virtual O transform(I i) = 0;
};

Далее, я хочу разрешить объединение Transform объектов в своего рода преобразование "в трубу":

template <typename X, typename Y, typename Z> class MergeTransform : public Transform<X, Z> {
private:
    Transform<X, Y> *first;
    Transform<Y, Z> *second;
public:
    MergeTransform(Transform<X, Y> *first, Transform<Y, Z> *second) {
        this->first = first;
        this->second = second;
    }
    Z transform(X x) {
        return second->transform(first->transform(x));
    }
};

Что меня озадачивает На данный момент пытается выяснить, как использовать шаблон для объединения произвольного числа преобразований. Я хотел сделать что-то вроде

template <typename A, typename B, typename C> Transform<A, C> *merge(Transform<A, B> *t1, Transform<B, C> *t2) {
    return new MergeTransform(t1, t2);
}
template <typename A, typename... Bs, typename C> Transform<A, C> *merge(/* what should go here? */) {
    // ...
}

, но потом я в растерянности от того, как представить список Transform s из A->B1, B1->B2, B2->...->Bn, Bn->C во втором merge методе. Я также подумал о том, чтобы позволить параметрам шаблона быть самими преобразованиями, ie.

/*
 * assume A and B are Transforms
 */
template <typename A, typename B> /* some return Transform type here */ *merge(A *a, B *b) {
    return new MergeTransform(a, b);
}
template <typename A, typename... Bs, typename C> /* return type */ *merge(A *a, Bs... bs, C *c) {
    return merge(merge(a, bs), c);
}

, но не могу определить тип возврата, не зная шаблонных типов A и B Transform s. Есть ли способ получить доступ к этим значениям? В идеале я мог бы сказать

template <Transform<X, Y> A, Transform<Y, Z> B> Transform<X, Z> *merge(A *a, B *b) {
    return new MergeTransform(a, b);
}
template <Transform<W, X> A, Transform<?, ?>... Bs, Transform<Y, Z> C> Transform<W, Z> *merge(A *a, Bs... *bs, C *c) {
    return merge(merge(a, bs), c);
}

, когда компилятор выдает ошибку, если расширение merge(a, bs) не вернуло Transform<X, Y>. Каков наилучший способ сделать это?

1 Ответ

0 голосов
/ 20 апреля 2020

При злоупотреблении перегрузкой операторов и выражением свертывания (C ++ 17) вы можете сделать что-то подобное для цепной операции:

template <typename ... Fs>
class Chain
{
private: 
    template <typename F>
    struct CallerWrap
    {
        F f;
        CallerWrap(F f) : f(f) {}

        template <typename T>
        friend decltype(auto) operator+ (const CallerWrap& callerWrap, T&& x)
        {
            return callerWrap.f(std::forward<T>(x));
        }
    };

    std::tuple<Fs...> tup;  
public:

    Chain(Fs... fs) : tup{fs... }{}

    template <typename T>
    decltype(auto) operator()(T x) const {
        return std::apply([&x](auto... fs){ return (CallerWrap{fs} + ... + x); }, tup);
    }

};

Демо

Для f1(f2(f3(x))) нет выражений сгиба, но есть для f1 + (f2 + (f3 + x)) (и других бинарных операторов). Поэтому мы добавляем обертку, чтобы использовать более позднюю и эквивалентную первой.

...