12

我正在尝试bar在另一个函数的参数 ( foo1/ foo2) 的上下文中解析重载函数 ( ) 的地址。

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通过 SFINAE 禁用除一个版本之外的所有版本的bar,无法编译并显示以下消息:

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);编译(或类似的东西)?

4

2 回答 2

2

如评论中所述,[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);正确解决。

于 2016-09-09T07:39:28.840 回答
1

某种一般的答案在这里:Expression SFINAE to overload on type of pass function pointer

对于实际情况,无需使用类型特征或decltype()- 旧的重载解决方案将为您选择最合适的函数并将其分解为“参数”和“返回类型”。只需枚举所有可能的调用约定

// 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, &&)

于 2015-11-20T02:38:07.863 回答