我的情况
我有两个类,由不受我控制的外部库提供,例如他们的界面对我来说是固定的。
第一个是模板化的二维容器类型,它允许我操作通过成员函数以及原始指针保存的内容。
第二个是一个包含一堆静态成员函数的类,这些函数抽象了一些经过 SIMD 优化的 vector 操作。它们中的大多数都被重载以采用不同的数据类型,但是它们都共享相同的接口(interface),例如
VecOperations::op (Type* dest, const Type* src, /* a variable number of operation specific arguments */, int len)
我想要达到的目标:
我想迭代我的 2D 容器的第一个维度,并在第二次迭代中对每个 vector 应用 vector 操作。因此我想更换例如
auto** ptrs = conatiner.getArrayOfRawPointers();
for (int i = 0; i < container.getXDim(); ++i)
VecOperations::foo (ptrs[i], ptrs[i], arg1, arg2, arg3, container.getYDim());
理想情况下是这样的(伪代码)
forAllElements<VecOperations::foo> (container, arg1, arg2, arg3);
这应该适用于存储在我的容器中的所有类型的类型,这些类型受 vector 操作类的支持,也适用于所有数量的 vector 操作特定参数。据我所知,写一些类似
forAllElements
以上是不可能的。我目前的解决方案:
我想出了这个:
template <typename ElemType, typename ...Args>
struct ForAllElemements
{
template <void(*op)(ElemType*, const ElemType*, Args..., int)>
static void call (Container<ElemType>& buffer, Args... args)
{
auto xDim = container.getXDim();
auto yDim = container.getYDim();
auto** ptrs = conatiner.getArrayOfRawPointers();
for (int i = 0; i < xDim; ++i)
op (ptrs[i], const_cast<const ElemType*>(ptrs[i]), args..., yDim);
}
};
这可以像这样使用
// using a Container<float> and VecOperations::foo (float*, const float*, float, int, float, int)
ForAllElemements<float, float, int, float>::call<VecOperations::foo> (container, arg1, arg2, arg3);
虽然在 C++17 中从构造函数中推导出类模板参数是可行的,但据我所知,从静态函数调用中推导出它是行不通的。据我了解,这根本没有定义,从技术上讲,我看不出任何原因
ForAllElemements::call<VecOperations::foo> (container, arg1, arg2, arg3);
应该是不可能的,因为所有模板类型都可以从传递给静态函数的参数中推断出来。
所以我问你,是否有任何我不知道的 super 聪明的解决方法或模式可以让 C++17 或更高版本的标准实现这样的事情?
最佳答案
类模板参数推导仅基于类对象的初始化程序发生。在这里,您甚至不需要类类型的对象,只需要使用静态成员函数。
但是备份,也许一个普通的函数模板可以工作:
// C++20 will define std::type_identity_t; or just define your own:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using type_identity_t = typename type_identity<T>::type;
template <typename ...Args, typename ElemType>
void forAllElements(
Container<ElemType> &c,
void (*op)(ElemType*, const ElemType*, type_identity_t<Args>..., int),
Args...);
函数指针在这里确实需要是函数参数而不是模板参数。这适用于普通
Args...
在函数指针签名中而不是 type_identity_t<Args>...
如果函数重载,但函数未重载,编译器可能需要 type_identity_t
,大概是为了确保 Args
是在一个非推断的上下文中。 (我认为标准中有一个不清楚的要求导致一些不同的结果......)备注
Args
只能从 forAllElements
的参数推导出来而不是来自函数类型,并且函数类型需要完全匹配。因此,如果您允许推导这些类型,则需要注意传入的表达式的确切类型。如有必要,请转换它们。如果使用文字作为常量值,您可以使用 1.0f
之类的形式获取 float
类型等。或者,您可以指定参数类型,如 forAllElements<float, int, float>
,这就是为什么我把 ...Args
之前 ElemType
在模板中(尽管现在 ElemType
永远不能明确给出,必须从容器参数中推导出来)。
关于c++ - 从静态成员函数推断类模板参数的模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60935388/