SFINAE和重载函数的地址
SFINAE and the address of an overloaded function
我正在尝试在另一个函数的参数(foo1
/foo2
)的上下文中解析重载函数(bar
)的地址。
struct Baz {};
int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}
void foo1(void (&)(Baz *)) {}
template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
int main() {
foo1(bar); // Works
foo2<Baz>(bar); // Fails
}
foo1
没有问题,它显式地指定了bar
的类型。
然而,除了一个版本的bar
之外,foo2
通过SFINAE对其自身禁用,无法编译,并显示以下消息:
main.cpp:19:5: fatal error: no matching function for call to 'foo2'
foo2<Baz>(bar); // Fails
^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
^
1 error generated.
我的理解是C++不能同时解析重载函数的地址和执行模板参数推导。
这是原因吗?有没有一种方法可以编译foo2<Baz>(bar);
(或类似的东西)?
正如评论中所提到的,[14-1.8.1/6](工作草案,从函数调用中推导模板参数)在这种情况下的规则(emphasis mine):
当p是函数类型、函数指针类型或指向成员函数类型的指针时:
如果参数是包含一个或多个函数模板的重载集,则该参数将被视为非推导上下文。
如果参数是重载集(不包含函数模板),则尝试使用该集的每个成员进行试参数推导。如果只有一个重载集成员的推导成功,则该成员将用作推导的参数值如果重载集的多个成员的推导成功,则参数将被视为非推导上下文
一旦扣除结束,SFINAE就会参与游戏,因此绕过标准规则也无济于事
有关进一步的详细信息,您可以看到上面链接的项目符号末尾的示例。
关于你的最后一个问题:
有没有办法编译
foo2<Baz>(bar);
(或类似的东西)?
两种可能的替代方案:
-
如果您不想修改
foo2
的定义,可以将其调用为:foo2<Baz>(static_cast<void(*)(Baz *)>(bar));
通过这种方式,您可以显式地从重载集中选择一个函数。
-
如果允许修改
foo2
,可以将其重写为:template <class T, class R> auto foo2(R(*d)(T*)) {}
它或多或少是以前的,在这种情况下没有
decltype
,并且是一个可以自由忽略的返回类型
实际上,你不需要使用任何SFINAE函数来实现这一点,推导就足够了
在这种情况下,foo2<Baz>(bar);
被正确地解析。
这里有一些常见的答案:表达式SFINAE对传递的函数指针的类型进行重载
对于实际情况,不需要使用类型traits或decltype()
——好的旧重载解析会为您选择最合适的函数,并将其分解为"arguments"answers"return type"。只需列举所有可能的调用约定
// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}
// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif
// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}
将它们包裹在模板结构中可能很有用
template<typename... T>
struct Foo2 {
// Common functions
template <typename R> static void foo2(R(*)(T*...)) {}
...
};
Zoo2<Baz>::foo2(bar);
尽管如此,由于成员函数具有修饰符(const
、volatile
、&&
)
- 如何在c++程序中找到函数的地址
- 函数名是c中该函数的第一条指令的地址吗
- 从 C++ 中的函数返回数组地址问题
- 为什么成员函数地址离自由函数这么远?
- 调用 lua 函数的地址为 C/C++?
- 在 Microsoft Access SQL 中调用自定义 DLL 函数时传递的内存地址无效
- 为什么每次执行时函数的地址都不同?
- 我正在尝试将表的地址传递给要在另一个函数中使用的指针,但得到不兼容的指针类型
- 获取函数的地址?
- 模板到函数的时刻地址是否可以作为指向某个函数的函数指针传递?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 指针在函数调用后更改其地址
- 在 gcc/clang (C++) 中获取函数范围之外的标签地址
- 在每个运行时将函数保存到相同的地址
- 在指向现有内存地址的 hpp 文件中声明成员函数的最佳方法
- 创建一个函数来转换数组元素的类型并返回数组的地址
- 如何定义全局函数指针并分配给特定地址
- 常量引用函数参数的地址何时唯一?
- 打印我的Get MAC地址函数的std::字符串时出现意外输出
- 成员函数地址(函数指针)