首页 > 解决方案 > C++ 可变参数类型实例化

问题描述

我一直在为此寻找解决方案,但一直未能找到令人满意的解决方案:

假设我有一个可变参数类型:

template<typename... layers_t>
class Layer_Aggregate
{

};

我有一些上面的别名,如下所示:

using stack_t = Layer_Aggregate<
    Matrix<3,3>,
    Matrix<3,3>
>;
  1. 我将如何正确实例化 layer_t 类型的对象?我不确定表示和实例化 Matrix<3,3> 的最佳方式是什么。有解决这个问题的设计模式吗?我可以轻松地实例化 stack_t,但它永远不会包含任何 Matrix<3,3> 数据,因为我的类不包含数据成员。
  2. 我的最终目标是利用模板表达式并允许诸如将所有矩阵相乘在一行中的操作。我不确定这是否会改变上述问题的答案。我正在使用的库评估赋值表达式。

标签: c++templatesvariadic-templates

解决方案


有几种方法可以定义折叠一堆对象的可变参数函数:

直接使用折叠表达式

如果您可以使用,这在 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

通过包装器使用折叠表达式

如果您的 reduce 函数不能作为运算符使用怎么办?例如:

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


推荐阅读