定义任何成员函数指针的别名中的模板实参演绎

Template argument deduction in alias typedefing any member function pointer

本文关键字:实参 演绎 别名 成员 函数 指针 定义 任何      更新时间:2023-10-16

在回答一个问题时,我建议使用模板别名对成员函数的签名进行类型定义;也就是说,不仅要定义成员函数的类型,还要能够分解出包含该方法的目标类:

template<typename T>
using memberf_pointer = int (T::*)(int, int); 

虽然这似乎涵盖了所问的问题,但我试图将其推广到任意函数参数:

template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...); 

由于参数演绎问题而失败(基本上它假设一个空的参数列表)。下面是一个演示:

#include <iostream>
class foo
{
public:
  int g (int x, int y) { return x + y ; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args&&...); 
int main()
{
  foo f ;
  memberf_pointer<foo> mp = &foo::g ;
  std::cout << (f.*mp) (5, 8) << std::endl ;
}

为什么会这样?有办法让它起作用吗?

问题的标题和正文中的措辞都很容易误导人。在您的示例中,任何地方都没有进行模板演绎。当你写:

memberf_pointer<foo> mp = &foo::g;

memberf_pointer<foo>是一个别名模板,是的,但它是一个别名模板的特定实例化。没有扣款,因为你提供的是确切的mp类型。这一行完全相当于:

int (foo:*mp)() = &foo::g;

无法编译,原因很明显,g接受参数。在赋值语句中获得模板演绎的方法是使用auto:

auto mp = &foo::g;

mp的类型将与您调用的U的类型相同:

template <typename U> void meow(U );
meow(&foo::g);

也就是int (foo::*)(int, int)

类似地,您可以这样做:

decltype(&foo::g) mp = &foo::g;

它会给你和之前相同的类型。

当然,即使你提供了正确的参数列表:

memberf_pointer<foo, int, int> mp = &foo::g;

仍然无法编译,因为别名向两个参数都添加了右值引用。mp的类型是int (foo::*)(int&&, int&&),这与&foo::g不匹配。也许你想把这当作是通过转发引用的推理,但这里的情况并非如此。为了正确使用别名,您必须重写它:

template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...); 
memberf_pointer<foo, int, int> mp = &foo::g;

如果有接受右值引用的成员函数,则可以显式地提供它:

class bar
{
public:
  int h(int&& x, int&& y) { return x + y ; }
};
memberf_pointer<bar, int&&, int&&> mb = &bar::h;

在这种情况下为什么不直接使用auto呢?在这种情况下使用模板的唯一优点是能够显式地提供类型。

另一方面,如果你想让你的模板自动推断一个函数类型,它需要在该类型上直接参数化。如果您还希望它为您提供所有构建块,最简单的方法是为函数或成员函数专门化它。例子:

template<typename T> struct memberf_pointer_descriptor;
template<typename TOwner, typename TRet, typename... Args>
struct memberf_pointer_descriptor<TRet(TOwner::*)(Args...)>
{
    // Your stuff goes here.
    using type = TRet(TOwner::*)(Args...);
};
memberf_pointer_descriptor<decltype(&foo::g)>;

或者直接接受foo::g作为参数的函数模板,以减少使用显式decltype的需要。

使您的示例工作的方法如下:

#include <iostream>
class foo
{
public:
    int g(int x, int y) { return x + y; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);
int main()
{
    foo f;
    memberf_pointer<foo, int, int> mp = &foo::g;
    std::cout << (f.*mp) (5, 8) << std::endl;
}

它删除可变模板形参上的引用,并且在实例化memberf_pointer时也提供成员函数形参。但auto可能是可行的…

c++没有辛德利-米尔纳类型的演绎系统,它不会为你特定的右值赋值工作

memberf_pointer<foo> mp = &foo::g ;

对于一些快速的变通方法,你可以

  1. 只需删除整个结构体并使用auto

    auto mp = &foo::g;
    
  2. 显式提供类型或指针类型

    template<typename T>
    using memberf_pointer = T;
    memberf_pointer<decltype(&foo::g)> mp = &foo::g;
    

病死率。模板参数演绎