我正在尝试在另一个函数的参数(bar
/ foo1
)的上下文中解析重载函数(foo2
)的地址。< / p>
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
的类型。
然而,foo2
除了bar
之外的所有版本都通过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);
(或类似的东西)编译?
答案 0 :(得分:1)
正如评论中所提到的,[14.8.2.1/6](工作草案,从函数调用中推断出模板参数)在这种情况下的规则(强调我的):
当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);
已正确解析。
答案 1 :(得分:0)
这里的一般答案是:Expression SFINAE to overload on type of passed function pointer
对于实际情况,不需要使用类型特征或decltype()
- 良好的旧重载分辨率将为您选择最合适的函数并将其分解为参数&#39 ;和&#39;返回类型&#39;。只列举所有可能的调用约定
// 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
,&&
)