如何将自由形式的谓词作为模板函数指针参数传递?

How can I pass a free-form predicate as a template function pointer argument?

我正在尝试编写一个函数,通过使用自由形式的谓词测试一系列对象来构造位掩码:

template<typename... Args, bool(*TestingFunc)(Object, Args...)>
inline uint32_t getMask(const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(TestingFunc(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

但是,上面的代码不起作用:gcc/clang 每次调用函数时都会报告一个 no matching function for call to(clang 的 note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Args',gcc 的 note: template argument deduction/substitution failed:;调用是 getMask<int, pred1>(objects, 10),谓词是 bool pred1(Object, int),请参阅下面的信息)。

有一些定义明确的谓词接受特定于 getMask() 调用的不同数量的附加参数。每个谓词都是一个简单的函数。理想情况下,应该编写 getMask() 函数,以便我可以像这样调用它:

/* bool predicate1(Object, size_t, size_t, int, int); */
mask = getMask<predicate1>(objects, a.height(), b.width(), 10, 15);
/* ... */
/* bool predicate2(Object, int, int); */
mask = getMask<predicate2>(objects, x, y);

这是我程序中的一个热点;性能至关重要。 getMask() 并且谓词用内联标记。代码必须用 C++11(不是 C++14 或更高版本)编写。

那么,getMask()应该怎么写呢?

从c++17你可以写

template<auto TestingFunc, typename... Args>
inline uint32_t getMask(const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(TestingFunc(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

在 C++11 中你需要做的更多:

首先,我们不能使用模板函数,因为我们需要对它进行部分重载。因此,只需采用具有静态功能的结构:

struct Object
{   
    Object( int _val ):val{_val}{}
    int val;
};  

template <class F, F f> struct caller;

template <class Obj, class... Args, bool (* TestingFunc)(Obj, Args...)>
struct caller<bool (*)(Obj, Args...), TestingFunc>
{   
    static uint32_t Do( std::vector<Object>& objects, Args... args) 
    {
        uint32_t mask = 0;

        for(size_t i = 0; i < objects.size(); i++)
            if(TestingFunc(objects[i], args...))
                mask |= 1 << i;

        return mask;
    }
};  

bool checkit( const Object& o, int i ) 
{   
    return o.val==i;
}   

int main()
{   
    std::vector<Object> objects;
    objects.emplace_back(1);
    objects.emplace_back(2);
    objects.emplace_back(3);

// The ugly thing before c++17 is, that you have to specify the
// function type itself as a template parameter. As said: C++17
// has offered exactly the feature of auto here in template parameter lists
// to allow deduction of type from parameter.
    std::cout << caller<decltype(&checkit), checkit>::Do(objects,3) << std::endl;
}

但我认为耗时的根本不是函数指针的使用。在你手动破解很多东西之前,衡量,衡量,衡量!

我建议您遵循标准库,只按值传递模板类型的可调用对象。如果它不是函数指针,它将被内联。

template<class... Args, class F>
inline uint32_t getMask(F f, const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(f(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

还有一种非常简单的方法可以将函数指针转换为无状态可调用对象,如果它是 ctce:
使用 std::integral_constant<decltype(p), p>。在 C++17 中,也许有一个助手。

template <auto f>
using to_type = std::integral_constant<decltype(f), f>;

getMask(to_type<checkit>(), somedata, arg0, arg1, arg2);
getMask([](auto... x){ return checkit(x...); }, somedata, arg0, arg1, arg2);