别名模板中的模板参数推导 - 类型定义任何成员函数指针
Template argument deduction in alias templates - typedefing any member function pointer
虽然 answering 一个问题,但我建议使用模板别名来对成员函数的 signature 进行类型定义;也就是说,不仅是类型定义成员函数,而且能够分解出包含方法的目标 class:
template<typename T>
using memberf_pointer = int (T::*)(int, int);
虽然这似乎涵盖了所问的问题,但我试图将其概括为任意函数参数:
template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...);
它因参数推导问题而失败(基本上它假设一个空参数列表)。这是 demo:
#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 ;
}
这是为什么?有没有办法让它工作?
在这种情况下为什么不直接使用汽车呢?在这种情况下使用模板的唯一优势是能够明确提供您的类型。
另一方面,如果您希望模板自动推导函数类型,则需要直接在该类型上进行参数化。如果您还希望它为您提供所有构建块,最简单的方法是将其专门用于函数或成员函数。示例:
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++ 没有 Hindley-Milner 类型推导系统之类的功能,它不适用于您的特定右值分配
memberf_pointer<foo> mp = &foo::g ;
至于一些快速解决方法,您可以
只需删除整个结构并使用 auto
auto mp = &foo::g;
明确提供类型或指针类型
template<typename T>
using memberf_pointer = T;
memberf_pointer<decltype(&foo::g)> mp = &foo::g;
问题的标题和 body 中的措辞都非常具有误导性。在您的示例中,任何地方都在进行零模板推导。当你写:
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
不匹配。也许您希望将其作为 forwarding-reference 的推论,但此处并非如此。为了正确使用别名,您必须重写它:
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;
虽然 answering 一个问题,但我建议使用模板别名来对成员函数的 signature 进行类型定义;也就是说,不仅是类型定义成员函数,而且能够分解出包含方法的目标 class:
template<typename T>
using memberf_pointer = int (T::*)(int, int);
虽然这似乎涵盖了所问的问题,但我试图将其概括为任意函数参数:
template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...);
它因参数推导问题而失败(基本上它假设一个空参数列表)。这是 demo:
#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 ;
}
这是为什么?有没有办法让它工作?
在这种情况下为什么不直接使用汽车呢?在这种情况下使用模板的唯一优势是能够明确提供您的类型。
另一方面,如果您希望模板自动推导函数类型,则需要直接在该类型上进行参数化。如果您还希望它为您提供所有构建块,最简单的方法是将其专门用于函数或成员函数。示例:
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++ 没有 Hindley-Milner 类型推导系统之类的功能,它不适用于您的特定右值分配
memberf_pointer<foo> mp = &foo::g ;
至于一些快速解决方法,您可以
只需删除整个结构并使用
auto
auto mp = &foo::g;
明确提供类型或指针类型
template<typename T> using memberf_pointer = T; memberf_pointer<decltype(&foo::g)> mp = &foo::g;
问题的标题和 body 中的措辞都非常具有误导性。在您的示例中,任何地方都在进行零模板推导。当你写:
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
不匹配。也许您希望将其作为 forwarding-reference 的推论,但此处并非如此。为了正确使用别名,您必须重写它:
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;