将泛型成员函数定义为模板参数,没有冗余成员签名作为参数

Define generic member function as template parameter without redundant member signature as parameter

我尝试编写一个通用成员 Delegate 函数(基于这个很酷的答案 generic member function pointer as a template parameter):

template <typename T, T>
class Delegate {};

template <typename T, typename R, typename... Args, R (T::*TMember)(Args...)>
class Delegate<R (T::*)(Args...), TMember> {
 public:
  Delegate(T &obj) : obj_{obj} {}

  R operator()(Args &&... args) const noexcept(
      noexcept((obj_.*TMember)(std::forward<Args>(args)...))) {
    return (obj_.*TMember)(std::forward<Args>(args)...);
  }

 private:
  T &obj_;
};

template <typename T, typename R, typename... Args, R (T::*TMember)(Args...) const>
class Delegate<R (T::*)(Args...) const, TMember> {
 public:
  Delegate(const T &obj) : obj_{obj} {}

  R operator()(Args &&... args) const noexcept(
      noexcept((obj_.*TMember)(std::forward<Args>(args)...))) {
    return (obj_.*TMember)(std::forward<Args>(args)...);
  }

 private:
  const T &obj_;
};

struct Foo {
  int Bar(int a, int b) noexcept { return a + b; }

  int ConstBar(int a, int b) const noexcept { return a + b; }
};

int main() {
  Foo obj;
  auto add = Delegate<int (Foo::*)(int, int), &Foo::Bar>(obj);

  std::cout << add(1, 2) << std::endl;  // 3

  const Foo const_obj;
  auto const_add =
      Delegate<int (Foo::*)(int, int) const, &Foo::ConstBar>(const_obj);

  std::cout << const_add(3, 4) << std::endl; // 7

  return 0;
}

所以,问题是:

  1. 我能否在 Delegate 实例化中以某种方式省略指向成员的模糊指针,如下所示:Delegate<Foo::Bar>(obj)?
  2. 我比较懒,指定两次成员签名对我来说太难了。我们能否基于单个模板参数以某种方式推断出模板签名,因此 Delegate<int (Foo::*)(int, int), &Foo::Bar>(obj) 变为 Delegate<&Foo::Bar>(obj)?
  3. 为了编写通用代码,我需要通过 noexcept(noexcept((obj_.*TMember)(std::forward<Args>(args)...)))noexcept 说明符从成员传播到 Delegateoperator()noexcept 说明符是否可以从 return 主体本身派生,而不是两次都指定主体?
  4. 我需要专门化两个版本 - const 和非 const 版本。编译器可以为我做这个吗? volatile / const volatile 专业化怎么样,我应该一次又一次地复制相同的代码吗?

如果不可能,请描述原因,或者至少,我可以在哪里阅读 :)

  1. Can i somehow omit fuzzy pointer to member in Delegate instantiation, like this: Delegate(obj)?

根据 this answer...我想答案是 "no"。

  1. I'm lazy, and specifying member signature two times looks too hard for me. Can we somehow deduce template signature based on single template argument, so Delegate(obj) becomes Delegate<&Foo::Bar>(obj)?

您标记了 C++17,所以...是的:您可以使用 auto

template <auto>
class Delegate
 { };

template <typename T, typename R, typename... Args,
          R (T::*TMember)(Args...)>
class Delegate<TMember>

// ...

template <typename T, typename R, typename... Args,
          R (T::*TMember)(Args...) const>
class Delegate<TMember>

// ...


   // ...    

   auto add = Delegate<&Foo::Bar>(obj);

   // ...

   auto const_add =
      Delegate<&Foo::ConstBar>(const_obj);
  1. I need to specialize both versions - const and non-const ones. May compiler do this for me? What about volatile / const volatile specializations, should i copy the same code again and again?

也许添加一个间接级别?

我的意思是...如果你为 Delegate 创建一个基础 class 如下

template <auto TMember, typename T, typename R, typename ... Args>
class DelegateBase
 {
   public:
      DelegateBase (T & obj) : obj_{obj}
       { }

      R operator() (Args &&... args) const
         noexcept
         (noexcept((std::declval<T>().*TMember)(std::forward<Args>(args)...)))
       { return (obj_.*TMember)(std::forward<Args>(args)...); }

   private:
      T & obj_;
 };

您可以使用 DelegateBase

编写 Delegate
template <auto>
class Delegate
 { };

template <typename T, typename R, typename... Args,
          R (T::*TMember)(Args...)>
class Delegate<TMember>
   : public DelegateBase<TMember, T, R, Args...>
 { using DelegateBase<TMember, T, R, Args...>::DelegateBase; };

template <typename T, typename R, typename... Args,
          R (T::*TMember)(Args...) const>
class Delegate<TMember>
   : public DelegateBase<TMember, T const, R, Args...>
 { using DelegateBase<TMember, T const, R, Args...>::DelegateBase; };

我想你可以添加几个 volatile/const volatile 专业。


题外话:如果你想使用完美转发,你必须使用转发引用,而不是右值引用。

我的意思是...您不能在 operator()

中使用完美转发
  R operator()(Args &&... args) const noexcept(
      noexcept((obj_.*TMember)(std::forward<Args>(args)...))) {
    return (obj_.*TMember)(std::forward<Args>(args)...);
  } // ....................^^^^^^^^^^^^^^^^^^ wrong

因为 Args... 包是 class 的可变参数,而不是运算符;所以在 operator() 中,Args && ... args 是右值引用(所以 std::move)。

如果要使用完美转发,就必须使用operator()本身的模板参数,所以

  template <typename ... As>
  R operator()(As &&... args) const noexcept(
      noexcept((obj_.*TMember)(std::forward<As>(args)...))) {
    return (obj_.*TMember)(std::forward<As>(args)...);
  } // ....................^^^^^^^^^^^^^^^^ ok