我正在编写一个库,其中包含许多函数对象,这些函数对象的类有几个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++对此类转换运算符没有问题。如果我使用单个模板参数,它将能够成功编译代码。那么,上面的代码应该被编译器接受还是拒绝?
template<class F> operator F*()
进行转换,添加 sfinae 测试以确保它是函数类型,使用 traits 提取参数,使用 helper 解包,使用 helper 转换为指针。 - Yakk - Adam Nevraumont