C++ 可变类型实例化
C++ Variadic Type Instantiation
我一直在寻找这个问题的解决方案,但一直未能找到令人满意的解决方案:
假设我有一个可变类型:
template<typename... layers_t>
class Layer_Aggregate
{
};
而且我有一些像这样的别名:
using stack_t = Layer_Aggregate<
Matrix<3,3>,
Matrix<3,3>
>;
- 我将如何正确实例化 layers_t 类型的对象?我不确定表示和实例化 Matrix<3,3> 的最佳方式是什么。有没有设计模式可以解决这个问题?我可以轻松实例化 stack_t,但它永远不会包含任何 Matrix<3,3> 数据,因为我的 class 不包含数据成员。
- 我的最终目标是利用模板表达式并允许在一行中进行诸如将所有矩阵相乘之类的操作。我不确定这是否会改变上述问题的答案。我正在使用的库在赋值时计算表达式。
有几种方法可以定义折叠一堆对象的可变参数函数:
直接使用折叠表达式
如果您可以的话,在 C++17 中这会变得更容易。只需为您的矩阵定义一个运算符:
Matrix<3, 3> operator *(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs) { /* ... */ }
然后就像使用fold expression
一样简单
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)
您仍然可以将它包装在辅助结构中并为 that:
定义一个运算符
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;
然后部分专门针对递归情况。这将提取第一个参数,并将其与其余参数的集合相乘。我们还可以使用相同的偏特化来推导运算符的 return 类型:
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,如果您要求编译器推断出 return 类型,则包装器很容易:
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);
我一直在寻找这个问题的解决方案,但一直未能找到令人满意的解决方案:
假设我有一个可变类型:
template<typename... layers_t>
class Layer_Aggregate
{
};
而且我有一些像这样的别名:
using stack_t = Layer_Aggregate<
Matrix<3,3>,
Matrix<3,3>
>;
- 我将如何正确实例化 layers_t 类型的对象?我不确定表示和实例化 Matrix<3,3> 的最佳方式是什么。有没有设计模式可以解决这个问题?我可以轻松实例化 stack_t,但它永远不会包含任何 Matrix<3,3> 数据,因为我的 class 不包含数据成员。
- 我的最终目标是利用模板表达式并允许在一行中进行诸如将所有矩阵相乘之类的操作。我不确定这是否会改变上述问题的答案。我正在使用的库在赋值时计算表达式。
有几种方法可以定义折叠一堆对象的可变参数函数:
直接使用折叠表达式
如果您可以的话,在 C++17 中这会变得更容易。只需为您的矩阵定义一个运算符:
Matrix<3, 3> operator *(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs) { /* ... */ }
然后就像使用fold expression
一样简单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)
您仍然可以将它包装在辅助结构中并为 that:
定义一个运算符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;
然后部分专门针对递归情况。这将提取第一个参数,并将其与其余参数的集合相乘。我们还可以使用相同的偏特化来推导运算符的 return 类型:
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,如果您要求编译器推断出 return 类型,则包装器很容易:
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);