简体   繁体   English

别名模板中的模板参数推导-类型化任何成员函数指针

[英]Template argument deduction in alias templates - typedefing any member function pointer

While answering a question, I proposed utilizing template aliases for typedefing the signature of a member function; 回答问题时,我建议利用模板别名来对成员函数的签名进行类型定义。 that is, not just typedefing a member function but being able to factor out the target class that contains the method: 也就是说,不仅要对成员函数进行类型定义,而且还可以分解出包含该方法的目标类:

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

Though this seems to cover what the question asked, I tried to generalize it for arbitrary function arguments: 尽管这似乎涵盖了问题的内容,但我还是尝试将其归纳为任意函数参数:

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

It fails with argument deduction issues (basically it assumes an empty arument list). 它因参数推导问题而失败(基本上它假定一个空的arument列表)。 Here's a 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 ;
}

Why is this? 为什么是这样? Is there a way to get it to work? 有没有办法让它工作?

Why not simply use an auto in this case? 为什么不在这种情况下简单地使用自动? The only advantage of having a template in this case is being able to provide your types explicitly. 在这种情况下,拥有模板的唯一优点是能够显式提供您的类型。

On the other hand, if you want your template to automatically deduce a function type, it needs to be directly parametrized on that type. 另一方面,如果您希望模板自动推导函数类型,则需要直接对该函数进行参数化。 If you also want it to provide all of the building blocks for you, the simplest way is to specialize it for functions or member functions. 如果您还希望它为您提供所有构建块,则最简单的方法是将其专门用于函数或成员函数。 Example: 例:

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)>;

Or a function template that directly takes foo::g as an argument, to mitigate the need of using an explicit decltype. 或者直接使用foo :: g作为参数的函数模板,以减轻使用显式decltype的需要。 Depends on your needs. 取决于您的需求。

A way to make your example work is the following: 使示例工作正常进行的方法如下:

#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;
}

It removes the reference on the variadic template parameter and when instantiating the memberf_pointer it supplies also the member function parameters. 它删除了可变参数模板参数上的引用,并在实例化memberf_pointer时也提供了成员函数参数。 But auto is probably the way to go... 但是auto可能是要走的路...

C++ doesn't feature something like a Hindley-Milner type deduction system and it won't work for your specific rvalue assignment C ++不具有类似于Hindley-Milner类型推导系统的功能,并且不适用于您的特定右值分配

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

As for some quick workarounds you could 至于一些快速的解决方法,您可以

  1. Just drop the whole struct and use auto 只需删除整个结构并使用auto

     auto mp = &foo::g; 
  2. Explicitly provide the types or the pointer type 明确提供类型或指针类型

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

Cfr. (CFR)。 Template argument deduction 模板参数推导

The wording in both the title and body of the question is very misleading. 问题的标题和正文中的措词都是极具误导性的。 There is zero template deduction going on in your example, anywhere. 在您的示例中,任何地方进行的模板归纳为零。 When you write: 当你写:

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

memberf_pointer<foo> is an alias template, yes, but it's a specific instantiation of one. memberf_pointer<foo>是别名模板,可以,但是它是一个特定的实例。 There is no deduction going on because you are providing the exact type of mp . 因为您提供的是mp的确切类型,所以没有任何推论。 That line is exactly equivalent to: 该行完全等效于:

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

which doesn't compile for the obvious reason that g takes arguments. 由于g接受参数的明显原因而无法编译。 The way to get template deduction in an assignment statement is to use auto : 在赋值语句中获得模板推论的方法是使用auto

auto mp = &foo::g;

The type of mp will be the same type as U had you called: mp的类型将与您调用的U相同:

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

which is to say, int (foo::*)(int, int) . 也就是说, int (foo::*)(int, int)

Similarly, you could do: 同样,您可以执行以下操作:

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

Which would give you the same type as before. 它将为您提供与以前相同的类型。

Of course, even if you provided the correct argument list: 当然,即使您提供了正确的参数列表:

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

That still wouldn't compile since your alias adds rvalue references to both arguments. 由于您的别名向两个参数都添加了右值引用,因此仍然无法编译。 The type of mp there is int (foo::*)(int&&, int&&) , which would not match &foo::g . 那里的mp类型是int (foo::*)(int&&, int&&) ,与&foo::g不匹配。 Perhaps you'd intended this to be deduction as if by forwarding-reference, but that is not the case here. 也许您打算像通过转发引用一样来推论这一点,但是在此情况并非如此。 In order to use the alias correctly you'd have to rewrite it: 为了正确使用别名,您必须重写它:

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

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

Had we had a member function that took an rvalue reference, we could then explicitly provide it: 如果我们有一个采用右值引用的成员函数,则可以显式提供它:

class bar
{
public:
  int h(int&& x, int&& y) { return x + y ; }
};

memberf_pointer<bar, int&&, int&&> mb = &bar::h;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM