变分函数指针转换

Variadic function pointer conversion

本文关键字:转换 指针 函数      更新时间:2023-10-16

我正在编写一个包含许多函数对象的库,这些函数对象的类有几个operator()重载,这些重载不依赖于类的状态,也不会改变它。现在,我试图让我的代码与许多老式的API一起工作(这不是随机的需要,我实际上必须处理这样的API),因此决定将函数对象转换为与其中一个重载对应的任何函数指针。在某个时刻,我意识到我有太多这样的函数指针运算符转换,理论上我应该能够编写一个变差转换运算符。这里有一个类实现了这样一个变差算子:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);
    template<typename... Args>
    operator fptr_t<Args...>() const
    {
        return [](Args... args) {
            // Whatever
        };
    }
};

正如您所看到的,我使用lambda到函数指针的转换来实现转换运算符,这不是问题,因为我拥有的每个函数对象都是无状态的。目标是能够使用如下类:

int main()
{
    void(*foo)(int) = foobar();
    void(*bar)(float, double) = foobar();
}

g++使用预期的语义编译此代码没有问题。然而,clang++以模板替换失败错误拒绝了它:

main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
    void(*foo)(int) = foobar();
          ^           ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
    operator fptr_t<Args...>() const
    ^
1 error generated.

注意,只要不涉及可变模板,clang++就不会有这样的转换运算符的问题。如果我使用单个模板参数,那么编译代码就没有问题。现在,编译器应该接受还是拒绝上面的代码?

lambda只有在不捕获的情况下才能转换为函数指针,因此您的代码应该可以工作。这在标准5.1.2/p6 Lambda表达式[expr.prim.Lambda]Emphasis Mine)中得到了证明:

没有的非泛型lambda表达式的闭包类型lambda捕获具有公共非虚拟非显式常量转换函数到指针,指向具有C++语言链接(7.5)的函数与闭包类型的函数相同的参数和返回类型呼叫接线员此转换函数返回的值应为调用时与调用闭包类型的函数调用运算符。

所以我会把它作为一个CLANG错误来归档

作为CLANG的解决方案,您可以将其转换为std::function,如下所示:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);
    template<typename... Args>
    operator std::function<void(Args...)>() const
    {
        return [](Args... args) {
            //...
        };
    }
};
int main()
{
    std::function<void(int)> f1 = foobar();
    std::function<void(double, float)> f2 = foobar();
    f1(1);
    f2(2.0, 1.0f);
}

实时演示