Существует несколько способов определения функции 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