【问题标题】:Pattern to deduce class template arguments from static member function从静态成员函数推断类模板参数的模式
【发布时间】:2020-03-30 16:47:36
【问题描述】:

我的情况

我有两个类,由不受我控制的外部库提供,例如他们的界面对我来说是固定的。

第一个是模板化的二维容器类型,它允许我操作通过成员函数以及原始指针保存的内容。 第二个是一个包含一堆静态成员函数的类,这些函数抽象了一些经过 SIMD 优化的向量操作。它们中的大多数都被重载以采用不同的数据类型,但是它们都共享相同的接口,例如

VecOperations::op (Type* dest, const Type* src, /* a variable number of operation specific arguments */, int len)

我想要达到的目标:

我想迭代我的 2D 容器的第一个维度,并在第二次迭代中对每个向量应用向量操作。因此我想替换例如

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);

这应该适用于存储在我的容器中的所有类型的类型,这些类型受向量操作类的支持,也适用于所有数量的向量操作特定参数。据我所知,写上面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);

应该是不可能的,因为所有模板类型都可以从传递给静态函数的参数中推断出来。

所以我问你,是否有任何我不知道的超级聪明的解决方法或模式可以使 C++17 或更高版本的标准成为可能?

【问题讨论】:

    标签: c++ templates c++17


    【解决方案1】:

    类模板参数推导仅基于类对象的初始化程序发生。这里你甚至不需要类类型的对象,只需要使用静态成员函数。

    但是备份,也许一个普通的函数模板可以工作:

    // 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&lt;Args&gt;...,但是当函数未重载时,编译器可能需要 type_identity_t,大概是为了确保 Args 在非-在那里推断上下文。 (我认为标准中有一个不明确的要求导致一些不同的结果......)

    注意Args只能从forAllElements的参数推导出来,不能从函数类型推导出来,而且函数类型需要完全匹配。因此,如果您允许推导这些类型,则需要注意传入的表达式的确切类型。如有必要,请转换它们。如果使用文字作为常量值,您可以使用1.0f 之类的形式来获取float 类型等。或者,您可以指定forAllElements&lt;float, int, float&gt; 之类的参数类型,这就是我将...Args 放在ElemType 之前的原因在模板中(尽管现在ElemType 永远不能明确给出,必须从容器参数中推导出来)。

    【讨论】:

    • 感谢您的回答。这个 type_identity 辅助结构解决方案是我不知道可能的部分......最终会将它添加到我个人的实用程序集合中,并在其他地方使用它来简化一些可变参数模板的东西
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    相关资源
    最近更新 更多