将泛型成员函数定义为模板参数,而不使用冗余成员签名作为参数



我尝试编写一个通用成员Delegate函数(基于这个很酷的答案通用成员函数指针作为模板参数(:

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. 以某种方式省略委托实例化中指向成员的模糊指针吗,如下所示:委托(obj(?

根据这个答案...我想答案是否定的。

  1. 我很懒,两次指定成员签名对我来说太难了。我们是否可以以某种方式根据单个模板参数推断模板签名,因此 Delegate(obj( 变为 Delegate<&amp;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. 我需要专门化两个版本 - 常量和非常量版本。编译器可以为我做到这一点吗?易失性/常量易失性专业化呢,我应该一次又一次地复制相同的代码吗?

也许添加间接级别?

我的意思是。。。如果按如下方式为Delegate创建基类

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专业。


题外话:如果要使用完美转发,则必须使用转发引用,而不是 r 值引用。

我的意思是。。。您无法在operator()中使用完美转发

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

pack 是类的可变参数,而不是运算符的可变参数;所以在operator()中,Args && ... args是 r 值引用(所以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

最新更新