【问题标题】:template argument deduction for pointer to member function?指向成员函数的指针的模板参数推导?
【发布时间】:2012-01-30 18:14:46
【问题描述】:

我正在尝试构建一个静态绑定的委托类,其中成员函数在编译时绑定,从而有助于优化。

我有以下代码,它完全按照我想要的方式工作:

#include <iostream>

namespace thr {

template<typename T, T func>
struct delegate;

template<typename R,
         typename C,
         typename... A,
         R  (C::* mem_fun)(A...)>
struct delegate<R(C::*)(A...), mem_fun>
{ 
  delegate(C* obj_)
    : _obj(obj_)
  {}

  R operator()(A... a)
  {
    return (_obj->*mem_fun)(a...);
  }
 private:
  C* _obj;
};

} // namespace thr

struct foo
{
  double bar(int i, int j)
  { 
    return (double)i / (double)j;
  } 
};

int main()
{
  foo f;

  typedef thr::delegate<decltype(&foo::bar), &foo::bar> cb;      
  cb c(&f);

  std::cout << c(4, 3);
  return 0;
}

不过用法不是很优雅:

thr::delegate<decltype(&foo::bar), &foo::bar>

我想使用一个函数模板,它推导出模板参数并返回一个委托实例;类似于(此代码无法编译):

template<typename C, typename T, T func>
thr::delegate<T, func> bind(T func, C* obj)
{
  return thr::delegate<decltype(func), func>(obj);
} 

这将允许更优雅的语法:

auto cb = bind(&foo::bar, &f);

是否可以在函数模板中推导出非类型参数?

我正在努力实现的目标是否可能?

【问题讨论】:

  • 有时可以在函数模板中推导出非类型参数,例如N可以推导出template &lt;size_t N&gt; size_t int_array_length(int (&amp;array_ref)[N]) {return N;}。但是当然N 函数签名中存在类型的一部分,尽管它本身不是类型。
  • 添加一个宏:#define MAKE_DELEGATE(x, y) thr::delegate&lt;decltype(&amp;x), &amp;x&gt;(y) 我看不到任何其他方法可以避免将函数命名两次。
  • @Iori:您确实意识到在您的示例中,T 将是 func 的类型,因此不需要 decltype :) ?

标签: c++ templates delegates c++11


【解决方案1】:

std::function 有帮助吗? http://www2.research.att.com/~bs/C++0xFAQ.html#std-function 你的例子看起来很接近。

我认为编译器提供的 STL 做了一些非常糟糕的事情来使其顺利运行。在放弃之前,您可能想看看一个例子。

编辑:我出去尝试了你试图完成的事情。我的结论是编译错误:

  • 绑定(委托)的返回类型必须命名指向成员的指针,因为这是您自己的要求。
  • bind 应该接受指向成员的指针的名称是优雅的(即您的要求)
  • 编译器要求您不要使用函数参数隐藏模板参数或在参数和返回类型中使用名称。

因此,您的一项要求必须满足。

编辑 2:我冒昧地更改了您的委托,因此 bind 可以按您的意愿工作。不过,绑定可能不是您的首要任务。

#include <iostream>

namespace thr {


template<typename C,typename R,typename... A>
struct delegate
{ 
 private:
  C* _obj;
  R(C::*_f)(A...);
  public:
  delegate(C* obj_,R(C::*f)(A...))
    : _obj(obj_),_f(f)
  {}

  R operator()(A... a)
  {
    return (_obj->*_f)(a...);
  }

};

} // namespace thr

template<class C,typename R,typename... A> thr::delegate<C,R,A...> bind(R(C::*f)(A...),C* obj){
    return thr::delegate<C,R,A...>(obj,f);
}

struct foo
{
  double bar(int i, int j)
  { 
    return (double)i / (double)j;
  }
};

int main()
{
  foo f;
  auto c = bind(&foo::bar, &f);
  std::cout << c(4, 6);
  return 0;
}

【讨论】:

  • std::function&lt;int(int)&gt; 在其模板参数中只接受类型,没有值。
  • 据我了解,本练习的重点不是明确键入委托类型。拍auto 应该是可能的而且足够了。
  • 我不这么认为。我认为目标是在不复制信息的基础上将指向成员函数的指针作为模板参数(如示例中所示)。
【解决方案2】:

可以在函数签名中推导出除类型之外的其他实体,但函数参数本身不能用作模板参数。

给定:

template <size_t I> struct Integral { static size_t const value = I; };

你可以拥有:

template <size_t N>
Integral<N> foo(char const (&)[N]);

但你不能拥有:

Integral<N> bar(size_t N);

在前一种情况下,N 作为数组的大小是参数类型的一部分,在后一种情况下,N 是参数本身。可以注意到,在前一种情况下,N出现在类型签名的模板参数列表中。

因此,如果你想要的确实是可能的,成员指针 value 必须作为函数签名的模板参数列表的一部分出现。

使用constexpr 可能是一种节省,它可以将常规值转换为适合模板参数的常量:

constexpr size_t fib(size_t N) { return N <= 1 ? 1 : fib(N-1) + fib(N-2); }

Integral<fib(4)> works;

但我不够精明,无法走那条路……

不过,我确实有一个简单的问题:您为什么认为这会加快速度?编译器非常擅长常量传播和内联,以至于当他们可以在编译时评估变量的动态类型时,能够内联对虚函数的调用。你确定这值得为此大费周章吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多