C ++ Variadi c Тип реализации - PullRequest
0 голосов
/ 20 января 2020

Я некоторое время искал решение для этого и не смог найти удовлетворительное решение этой проблемы:

Предположим, у меня есть тип variadi c:

template<typename... layers_t>
class Layer_Aggregate
{

};

И у меня есть некоторые псевдонимы выше, как это:

using stack_t = Layer_Aggregate<
    Matrix<3,3>,
    Matrix<3,3>
>;
  1. Как бы я go о правильной реализации объекта типа layer_t? Я не уверен, каков наилучший способ представления и создания экземпляров Матрицы <3,3>. Есть ли шаблон дизайна для решения этой проблемы? Я могу легко создать экземпляр stack_t, но он никогда не будет содержать никаких данных Matrix <3,3>, потому что мой класс не содержит члена данных.
  2. Моя конечная цель - использовать преимущества шаблонных выражений и учесть такие операции, как умножение всех матриц в одну строку. Я не уверен, изменит ли это ответ на вышеуказанный вопрос. Используемая мной библиотека оценивает выражение при присваивании.

1 Ответ

0 голосов
/ 21 января 2020

Существует несколько способов определения функции variadi c, которая сворачивает несколько объектов:

Непосредственное использование выражений Fold

Это стало намного проще в C ++ 17, если это доступно для вас. Просто определите оператор для вашей матрицы:

Matrix<3, 3> operator *(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs) { /* ... */ }

Тогда это так же просто, как использовать выражение сгиба

template <typename... Ts>
auto aggregate(Ts&&... ts) {
    return (ts * ...);
}

, которое сделает правильное сгиб по всем матрицы, пока они не объединены в одну.

Использование:

Matrix<3, 3> a = /*...*/;
Matrix<3, 3> b = /*...*/;
Matrix<3, 3> c = /*...*/;
auto abc = aggregate(a, b, c);

Демонстрация: https://godbolt.org/z/uf54Hk

Использование выражений сгиба через оболочку

Что если вы уменьшите функция недоступна как оператор? Например:

Matrix<3, 3> multiply(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs)

Вы все еще можете обернуть его в вспомогательную структуру и определить оператор для , который :

template <auto Op, typename T>
struct RefWrapper { const T& ref; };

template <auto Op, typename T, typename U>
auto operator *(RefWrapper<Op, T> t, RefWrapper<Op, U> u) {
    return RefWrapper<Op, decltype(Op(t.ref, u.ref))>{ Op(t.ref, u.ref) };
}

template <auto Op, typename... Ts>
auto aggregate(Ts&&... ts) {
    return (RefWrapper<Op, Ts>{ ts } * ...).ref;
}

Демо: https://godbolt.org/z/APhT-v

C ++ 11 и C ++ 14 - Рекурсивные шаблоны

В C ++ 11/14 это немного сложнее, так как вам приходится вручную рекурсировать через каждый аргумент , Вы также не можете автоматически выводить сигнатуры указателей функций, поэтому для вывода поля необходимо добавить отдельный аргумент шаблона.

Начните с структуры-заполнителя.

template <typename Sig, Sig Op, typename... Ts>
struct LayerAggregate;

Затем частично специализируемся на рекурсивном случае. Это вырвет первый аргумент и умножит его на совокупность оставшихся аргументов. Мы также можем использовать ту же частичную специализацию, чтобы вывести тип возврата оператора:

template <typename T, T (*Op)(const T&, const T&), typename TFirst, typename... TRest>
struct LayerAggregate<T(*)(const T&, const T&), Op, TFirst, TRest...> {
    T operator()(TFirst&& first, TRest&&... rest) {
        T restAggregate = LayerAggregate<T(*)(const T&, const T&), Op, TRest...>{}(std::forward<TRest>(rest)...);
        return Op(first, restAggregate);
    }
};

Наконец, добавьте регистр терминала, чтобы прекратить повторение при получении последнего элемента:

template <typename T, T (*Op)(const T&, const T&), typename TLast>
struct LayerAggregate<T(*)(const T&, const T&), Op, TLast> {
    T operator()(TLast&& last) {
        return last;
    }
};

Если вы используете C ++ 14, обертка легко, если вы попросите компилятор определить тип возвращаемого значения:

template <typename Sig, Sig Op, typename... Ts>
auto Aggregate(Ts&&... ts) {
    return LayerAggregate<Sig, Op, Ts...>{}(std::forward<Ts>(ts)...);
}

Для C ++ 11 вам все равно придется вывести его самостоятельно хотя:

template <typename Sig, Sig Op, typename... Ts>
struct OpResult;

template <typename Sig, Sig Op, typename T, typename... Ts>
struct OpResult<Sig, Op, T, Ts...> {
    using Type = decltype(Op(std::declval<T>(), std::declval<T>()));
};

template <typename Sig, Sig Op, typename... Ts>
using OpResultT = typename OpResult<Sig, Op, Ts...>::Type;

template <typename Sig, Sig Op, typename... Ts>
OpResultT<Sig, Op, Ts...> Aggregate(Ts&&... ts) {
    return LayerAggregate<Sig, Op, Ts...>{}(std::forward<Ts>(ts)...);
}

Наконец, для вызова нужно просто передать сигнатуру функции, а также функцию:

Matrix<3, 3> a = /*...*/;
Matrix<3, 3> b = /*...*/;
Matrix<3, 3> c = /*...*/;
auto abc = Aggregate<decltype(&multiply), &multiply>(a, b, c);

Демо: https://godbolt.org/z/49wb7H

...