【问题标题】:Template argument deduction in alias templates - typedefing any member function pointer别名模板中的模板参数推导 - 定义任何成员函数指针
【发布时间】:2015-10-17 11:00:48
【问题描述】:

answering 提出问题时,我建议使用模板别名来定义成员函数的签名;也就是说,不仅仅是定义一个成员函数,而且能够分解出包含该方法的目标类:

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

这是为什么?有没有办法让它工作?

【问题讨论】:

  • 为什么不直接使用autoauto mp = &amp;foo::g ; 应该可以工作。
  • 分配右值不是成员函数参数的推导上下文。不过有一些解决方法:Solution 1Solution 2
  • @Nawaz 我正在使用命令模式并想确保我将接收器的成员函数传递给命令对象的构造。在那种情况下,拥有一个“有意识”的 typedef 会很不错(另一种选择是构建一个特征系统或使用概念)。 auto 太棒了,可惜不是我的情况

标签: c++ templates c++11 c++14 template-argument-deduction


【解决方案1】:

问题标题和正文中的措辞非常具有误导性。在您的示例中,任何地方都在进行零模板扣除。当你写:

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

memberf_pointer&lt;foo&gt; 是一个别名模板,是的,但它是一个特定的实例化。由于您提供的是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&amp;&amp;, int&amp;&amp;),与 &amp;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;

【讨论】:

  • 我可以对memberf_pointer&lt;foo&gt; mp = &amp;foo::g 是一个右值赋值发表评论吗(参见marco 的回答)?
  • @NikosAthanasiou &amp;foo::g 是一个右值,但这在这里不相关(如果你有auto fg = &amp;foo::g; memberf_pointer&lt;foo&gt; mp = fg,这将是一个“左值赋值”并且仍然由于完全相同的原因而失败)。重点是明确指定类型时没有类型推导。
【解决方案2】:

在这种情况下,为什么不简单地使用 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 的需要。取决于你的需要。

【讨论】:

  • 这里只为成员函数实现identity类型。
  • 我正在使用命令模式并想确保我将接收器的成员函数传递给命令对象的构造。在那种情况下,拥有一个“有意识的” typedef 会很不错(另一种选择是构建一个特征系统或使用概念)。 auto 太棒了,可惜不是我的情况
【解决方案3】:

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

#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 可能是要走的路……

【讨论】:

  • 我正在使用命令模式并想确保我将接收器的成员函数传递给命令对象的构造。在那种情况下,拥有一个“有意识”的 typedef 会很不错(另一种选择是构建一个特征系统或使用概念)。 auto 太棒了,可惜不是我的情况
【解决方案4】:

C++ 没有类似Hindley-Milner 类型推导系统的功能,它不适用于您的特定右值分配

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;
    

参照。 Template argument deduction

【讨论】:

  • 不错的解决方法。 memberf_pointer&lt;foo&gt; mp = &amp;foo::g ; 是如何分配右值的?
  • @NikosAthanasiou 这是int (foo::*)(int, int)类型的右值
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多