将泛型成员函数定义为模板参数,没有冗余成员签名作为参数
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;
}
所以,问题是:
- 我能否在
Delegate
实例化中以某种方式省略指向成员的模糊指针,如下所示:Delegate<Foo::Bar>(obj)
?
- 我比较懒,指定两次成员签名对我来说太难了。我们能否基于单个模板参数以某种方式推断出模板签名,因此
Delegate<int (Foo::*)(int, int), &Foo::Bar>(obj)
变为 Delegate<&Foo::Bar>(obj)
?
- 为了编写通用代码,我需要通过
noexcept(noexcept((obj_.*TMember)(std::forward<Args>(args)...)))
将 noexcept
说明符从成员传播到 Delegate
的 operator()
。 noexcept
说明符是否可以从 return
主体本身派生,而不是两次都指定主体?
- 我需要专门化两个版本 -
const
和非 const
版本。编译器可以为我做这个吗? volatile
/ const volatile
专业化怎么样,我应该一次又一次地复制相同的代码吗?
如果不可能,请描述原因,或者至少,我可以在哪里阅读 :)
- Can i somehow omit fuzzy pointer to member in Delegate instantiation, like this: Delegate(obj)?
根据 this answer...我想答案是 "no"。
- 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);
- 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
我尝试编写一个通用成员 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;
}
所以,问题是:
- 我能否在
Delegate
实例化中以某种方式省略指向成员的模糊指针,如下所示:Delegate<Foo::Bar>(obj)
? - 我比较懒,指定两次成员签名对我来说太难了。我们能否基于单个模板参数以某种方式推断出模板签名,因此
Delegate<int (Foo::*)(int, int), &Foo::Bar>(obj)
变为Delegate<&Foo::Bar>(obj)
? - 为了编写通用代码,我需要通过
noexcept(noexcept((obj_.*TMember)(std::forward<Args>(args)...)))
将noexcept
说明符从成员传播到Delegate
的operator()
。noexcept
说明符是否可以从return
主体本身派生,而不是两次都指定主体? - 我需要专门化两个版本 -
const
和非const
版本。编译器可以为我做这个吗?volatile
/const volatile
专业化怎么样,我应该一次又一次地复制相同的代码吗?
如果不可能,请描述原因,或者至少,我可以在哪里阅读 :)
- Can i somehow omit fuzzy pointer to member in Delegate instantiation, like this: Delegate(obj)?
根据 this answer...我想答案是 "no"。
- 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);
- 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