将来自任意 class 的成员函数指针存储为 class 实例变量
Storing member function pointer from arbitrary class as class instance variable
SO 上有几个问题将函数指针传递为 parameters/arguments(here, here, here, etc.). In fact, I asked a 前几天。但是,这个问题有点不同。
我的问题是我正在写一个class,我想非常灵活。
我现在所拥有的适用于 non-member 功能。贴在下面
template <typename T>
class MyClass
{
private:
typedef double (*firstFunctionPtr) (const T &var);
typedef bool (*secondFunctionPtr)(const T &var);
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc,
secondFunctionPtr secondFunc);
};
template<typename T>
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc),
{}
但是,当我需要使用指向其他任意 class 的成员函数的指针进行初始化时,这会崩溃,不幸的是,对于我来说,这恰好是我的目的的常见用例.
This answer 建议
In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types.
但是,我无法编译。我试过模板化我的 typedef(使用 C++11 别名方法),我试过向 class 添加第二个模板参数来处理这些成员函数的调用 class,但是两种方法都无效。
This Q/A 似乎正朝着我想做的方向发展,但我无法弄清它的正反面。
- 有人可以解释我如何修改我的 class 来处理传入的任意成员函数指针吗?
- 此外,是否可以使其可以处理任意成员函数或non-member函数?
- 最后,是否可以使用模板来实现?
郑重声明,我尽量避免使用 functional
header,但不使用它可能是愚蠢的事。
您需要做的就是bind
成员函数指针的对象实例作为第一个参数。
struct foo {
float bar1(const type &var);
bool bar2(const type &var);
};
foo my_foo;
auto f1 = std::bind(&foo::bar1, my_foo, _1);
auto f2 = std::bind(&foo::bar2, my_foo, _1);
MyClass<type> my_obj(f1, f2);
如果你想让MyClass
成为一个既能兼顾自由功能的模板
类型指针:
double (*)(const T &var);
bool (*)(const T &var);
对于某些参数类型 T
,或者 member-function
类型指针:
double (C::*)(const T &var);
bool (C::*)(const T &var);
对于某些参数类型 C
和 T
那么, MyClass
必须被参数化
T
和 C
并且你需要两个专业:
- 其中
C
是一些 non-class 类型
- 其中
C
是任何 class 类型
在情况(1)中,non-class类型C
不可能有成员函数,
这样就可以实现 free-function 指针特化。
在情况 (2) 中,class C
可能是一个具有成员函数的函数,因此一个
将实现 member-function 指针特化。
non-class 类型 C
的明显选择是 void
。所以我们可以C
默认为 void
:
主模板
template<typename T, typename C = void>
struct MyClass;
这样:
MyClass<T>
将是 T
的自由函数指针特化,并且:
MyClass<T,C>
对于 void
以外的任何 C
,都将是 member-function 指针特化。
如您所知,您可以使用 std::enable_if
和 SFINAE
使编译器
选择 class 模板的一种专业化或另一种专业化,具体取决于一个
它的模板参数 U
满足一些编译时测试。你可以拿
此处的方法,但另一种不需要该设备的方法可用:
从主模板开始,我们只想拥有:
自由函数专业化
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
和:
成员函数特化
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
但我们不能只这样,因为成员函数 "specialization" 恰好有
与主模板相同的模板参数。这意味着它 不是
专业化,编译器不允许。
不过,您可以轻松解决该问题,只需将主
template 一个它不需要的默认模板参数,但是它的
存在允许这两个专业化。
新建主模板
template <typename T, typename C = void, typename Default = void>
struct MyClass;
所以这是一个说明性的解决方案:
// Primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
// Free function specialization
template <typename T>
struct MyClass<T>
{
using firstFunctor_t = double(*)(T const &);
using secondFunctor_t = bool(*)(T const &);
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(T const & var) {
return _firstFunc(var);
}
bool callSecond(T const & var) {
return _secondFunc(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
using firstFunctor_t = double(C::*)(T const &);
using secondFunctor_t = bool(C::*)(T const &) const;
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(C & obj, T const & var) {
return (obj.*_firstFunc)(var);
}
double callFirst(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_firstFunc)(var);
}
bool callSecond(C & obj, T const & var) {
return (obj.*_secondFunc)(var);
}
bool callSecond(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_secondFunc)(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
在成员函数专业化中,请注意您可能会遇到的几点
没有考虑过:-
我决定要存储的第二个成员函数应该是
const 成员函数。 C
的成员函数很有可能
采用 T const &
参数并且 returns bool 将是 const
成员
功能,不是吗?如果是这样,那么 const-ness
必须是
我在专业化中使用的 member-function 类型定义:
using secondFunctor_t = bool(C::*)(T const &) const;
或尝试用任何 bool (C::*)(T const &) const
实例化特化
将无法编译。
另外,我为每个 MyClass<T,C>::callFirst
提供了两个重载
和 MyClass<T,C>::callSecond
,一个带有参数:
C & obj, T const & var
和另一个有参数的:
C const & obj, T const & var
没有第二个,尝试调用 MyClass<T,C>::callFirst
或 MyClass<T,C>::callSecond
与 obj
是 const 将失败
编译。
对于演示此解决方案的程序,您可以附加:
#include <iostream>
#include <string>
double foo(std::string const & s)
{
return std::stod(s);
}
bool bar(std::string const & s)
{
return s.size() > 0;
}
struct SomeClass
{
SomeClass(){};
double foo(std::string const & s) {
return ::foo(s);
}
bool bar(std::string const & s) const {
return ::bar(s);
}
};
int main()
{
MyClass<std::string> my0{foo,bar};
std::cout << std::boolalpha;
std::cout << my0.callFirst("1.11") << std::endl;
std::cout << my0.callSecond("Hello World") << std::endl;
MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
SomeClass thing;
std::cout << my1.callFirst(thing,"2.22") << std::endl;
std::cout << my1.callSecond(thing,"Hello World") << std::endl;
SomeClass const constThing;
std::cout << my1.callFirst(constThing,"3.33") << std::endl;
std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
return 0;
}
您说您希望此模板为 "extremely flexible"。这
图示解决方案适合您的示例,但您可能
有兴趣知道它 nearly 没有您所能得到的那么灵活。
对于自由函数和成员函数,附加 variadic template
参数,您的模板可以存储和调用 [member] 函数
任意 return 类型和任意数量的任意类型参数。
参见 和
回答。
我建议创建一个帮助对象来存储您要使用的类型:
template <typename RETURN, typename TYPE, typename CLASS>
struct function_pointer
{ using type_t = RETURN (CLASS::*)(const TYPE &); };
template <typename RETURN, typename TYPE>
struct function_pointer<RETURN, TYPE, std::nullptr_t>
{ using type_t = RETURN (*)(const TYPE &); };
如果将 class 作为第三个参数提供,则此类型将创建 member-function-pointer,否则将创建 function-pointer。现在,我们可以在 MyClass
:
中使用这个助手
template <typename T, typename CLASS = std::nullptr_t>
class MyClass
{
using firstFunctionPtr = typename function_pointer<double, T, CLASS>::type_t;
using secondFunctionPtr = typename function_pointer<bool, T, CLASS>::type_t;
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
void call_first(CLASS &c, const T&v) { (c.*_firstFunc)(v); }
void call_second(CLASS &c, const T&v) { (c.*_secondFunc)(v); }
void call_first(const T&v) { (_firstFunc)(v); }
void call_second(const T&v) { (_secondFunc)(v); }
};
我添加了 call_*
函数只是为了展示一个用例,如下所示:
// Some class with the expected function signatures
struct S1
{
int i = 0;
double d(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Another class with the expected function signatures
struct S2
{
double d(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Free function with which could have the expected function signature
template <typename R>
R f(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
使用 MyClass
和任意 class (S1
):
S1 a{1}, b{2};
S2 c, d;
MyClass<int, S1> MCiS1(&S1::d, &S1::b);
MCiS1.call_first(a, 111); // Prints -> 1 double S1::d(const int&)
MCiS1.call_second(b, 222); // Prints -> 2 bool S1::b(const int&)
MCiS1.call_first(c, 111); // Error decltype(c) is not S1.
MCiS1.call_second(d, 222); // Error decltype(d) is not S1.
使用 MyClass
和不同的 class (S2
):
MyClass<int, S2> MCiS2(&S2::d, &S2::b);
MCiS2.call_first(c, 111); // Prints -> double S2::d(const int&)
MCiS2.call_second(d, 222); // Prints -> bool S2::b(const int&)
MCiS2.call_first(a, 111); // Error decltype(c) is not S2.
MCiS2.call_second(b, 222); // Error decltype(d) is not S2.
将MyClass
与non-member函数结合使用:
MyClass<int> MCi(f<double>, f<bool>);
MCi.call_first(111); // Prints -> R f(const int&) [with R = double]
MCi.call_second(222); // Prints -> R f(const int&) [with R = bool]
查看现场演示 Here.
SO 上有几个问题将函数指针传递为 parameters/arguments(here, here, here, etc.). In fact, I asked a
我的问题是我正在写一个class,我想非常灵活。
我现在所拥有的适用于 non-member 功能。贴在下面
template <typename T>
class MyClass
{
private:
typedef double (*firstFunctionPtr) (const T &var);
typedef bool (*secondFunctionPtr)(const T &var);
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc,
secondFunctionPtr secondFunc);
};
template<typename T>
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc),
{}
但是,当我需要使用指向其他任意 class 的成员函数的指针进行初始化时,这会崩溃,不幸的是,对于我来说,这恰好是我的目的的常见用例.
This answer 建议
In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types.
但是,我无法编译。我试过模板化我的 typedef(使用 C++11 别名方法),我试过向 class 添加第二个模板参数来处理这些成员函数的调用 class,但是两种方法都无效。
This Q/A 似乎正朝着我想做的方向发展,但我无法弄清它的正反面。
- 有人可以解释我如何修改我的 class 来处理传入的任意成员函数指针吗?
- 此外,是否可以使其可以处理任意成员函数或non-member函数?
- 最后,是否可以使用模板来实现?
郑重声明,我尽量避免使用 functional
header,但不使用它可能是愚蠢的事。
您需要做的就是bind
成员函数指针的对象实例作为第一个参数。
struct foo {
float bar1(const type &var);
bool bar2(const type &var);
};
foo my_foo;
auto f1 = std::bind(&foo::bar1, my_foo, _1);
auto f2 = std::bind(&foo::bar2, my_foo, _1);
MyClass<type> my_obj(f1, f2);
如果你想让MyClass
成为一个既能兼顾自由功能的模板
类型指针:
double (*)(const T &var);
bool (*)(const T &var);
对于某些参数类型 T
,或者 member-function
类型指针:
double (C::*)(const T &var);
bool (C::*)(const T &var);
对于某些参数类型 C
和 T
那么, MyClass
必须被参数化
T
和 C
并且你需要两个专业:
- 其中
C
是一些 non-class 类型 - 其中
C
是任何 class 类型
在情况(1)中,non-class类型C
不可能有成员函数,
这样就可以实现 free-function 指针特化。
在情况 (2) 中,class C
可能是一个具有成员函数的函数,因此一个
将实现 member-function 指针特化。
non-class 类型 C
的明显选择是 void
。所以我们可以C
默认为 void
:
主模板
template<typename T, typename C = void>
struct MyClass;
这样:
MyClass<T>
将是 T
的自由函数指针特化,并且:
MyClass<T,C>
对于 void
以外的任何 C
,都将是 member-function 指针特化。
如您所知,您可以使用 std::enable_if
和 SFINAE
使编译器
选择 class 模板的一种专业化或另一种专业化,具体取决于一个
它的模板参数 U
满足一些编译时测试。你可以拿
此处的方法,但另一种不需要该设备的方法可用:
从主模板开始,我们只想拥有:
自由函数专业化
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
和:
成员函数特化
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
但我们不能只这样,因为成员函数 "specialization" 恰好有 与主模板相同的模板参数。这意味着它 不是 专业化,编译器不允许。
不过,您可以轻松解决该问题,只需将主 template 一个它不需要的默认模板参数,但是它的 存在允许这两个专业化。
新建主模板
template <typename T, typename C = void, typename Default = void>
struct MyClass;
所以这是一个说明性的解决方案:
// Primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
// Free function specialization
template <typename T>
struct MyClass<T>
{
using firstFunctor_t = double(*)(T const &);
using secondFunctor_t = bool(*)(T const &);
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(T const & var) {
return _firstFunc(var);
}
bool callSecond(T const & var) {
return _secondFunc(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
using firstFunctor_t = double(C::*)(T const &);
using secondFunctor_t = bool(C::*)(T const &) const;
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(C & obj, T const & var) {
return (obj.*_firstFunc)(var);
}
double callFirst(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_firstFunc)(var);
}
bool callSecond(C & obj, T const & var) {
return (obj.*_secondFunc)(var);
}
bool callSecond(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_secondFunc)(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
在成员函数专业化中,请注意您可能会遇到的几点 没有考虑过:-
我决定要存储的第二个成员函数应该是
const 成员函数。 C
的成员函数很有可能
采用 T const &
参数并且 returns bool 将是 const
成员
功能,不是吗?如果是这样,那么 const-ness
必须是
我在专业化中使用的 member-function 类型定义:
using secondFunctor_t = bool(C::*)(T const &) const;
或尝试用任何 bool (C::*)(T const &) const
实例化特化
将无法编译。
另外,我为每个 MyClass<T,C>::callFirst
提供了两个重载
和 MyClass<T,C>::callSecond
,一个带有参数:
C & obj, T const & var
和另一个有参数的:
C const & obj, T const & var
没有第二个,尝试调用 MyClass<T,C>::callFirst
或 MyClass<T,C>::callSecond
与 obj
是 const 将失败
编译。
对于演示此解决方案的程序,您可以附加:
#include <iostream>
#include <string>
double foo(std::string const & s)
{
return std::stod(s);
}
bool bar(std::string const & s)
{
return s.size() > 0;
}
struct SomeClass
{
SomeClass(){};
double foo(std::string const & s) {
return ::foo(s);
}
bool bar(std::string const & s) const {
return ::bar(s);
}
};
int main()
{
MyClass<std::string> my0{foo,bar};
std::cout << std::boolalpha;
std::cout << my0.callFirst("1.11") << std::endl;
std::cout << my0.callSecond("Hello World") << std::endl;
MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
SomeClass thing;
std::cout << my1.callFirst(thing,"2.22") << std::endl;
std::cout << my1.callSecond(thing,"Hello World") << std::endl;
SomeClass const constThing;
std::cout << my1.callFirst(constThing,"3.33") << std::endl;
std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
return 0;
}
您说您希望此模板为 "extremely flexible"。这
图示解决方案适合您的示例,但您可能
有兴趣知道它 nearly 没有您所能得到的那么灵活。
对于自由函数和成员函数,附加 variadic template
参数,您的模板可以存储和调用 [member] 函数
任意 return 类型和任意数量的任意类型参数。
参见
我建议创建一个帮助对象来存储您要使用的类型:
template <typename RETURN, typename TYPE, typename CLASS>
struct function_pointer
{ using type_t = RETURN (CLASS::*)(const TYPE &); };
template <typename RETURN, typename TYPE>
struct function_pointer<RETURN, TYPE, std::nullptr_t>
{ using type_t = RETURN (*)(const TYPE &); };
如果将 class 作为第三个参数提供,则此类型将创建 member-function-pointer,否则将创建 function-pointer。现在,我们可以在 MyClass
:
template <typename T, typename CLASS = std::nullptr_t>
class MyClass
{
using firstFunctionPtr = typename function_pointer<double, T, CLASS>::type_t;
using secondFunctionPtr = typename function_pointer<bool, T, CLASS>::type_t;
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
void call_first(CLASS &c, const T&v) { (c.*_firstFunc)(v); }
void call_second(CLASS &c, const T&v) { (c.*_secondFunc)(v); }
void call_first(const T&v) { (_firstFunc)(v); }
void call_second(const T&v) { (_secondFunc)(v); }
};
我添加了 call_*
函数只是为了展示一个用例,如下所示:
// Some class with the expected function signatures
struct S1
{
int i = 0;
double d(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Another class with the expected function signatures
struct S2
{
double d(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Free function with which could have the expected function signature
template <typename R>
R f(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
使用 MyClass
和任意 class (S1
):
S1 a{1}, b{2};
S2 c, d;
MyClass<int, S1> MCiS1(&S1::d, &S1::b);
MCiS1.call_first(a, 111); // Prints -> 1 double S1::d(const int&)
MCiS1.call_second(b, 222); // Prints -> 2 bool S1::b(const int&)
MCiS1.call_first(c, 111); // Error decltype(c) is not S1.
MCiS1.call_second(d, 222); // Error decltype(d) is not S1.
使用 MyClass
和不同的 class (S2
):
MyClass<int, S2> MCiS2(&S2::d, &S2::b);
MCiS2.call_first(c, 111); // Prints -> double S2::d(const int&)
MCiS2.call_second(d, 222); // Prints -> bool S2::b(const int&)
MCiS2.call_first(a, 111); // Error decltype(c) is not S2.
MCiS2.call_second(b, 222); // Error decltype(d) is not S2.
将MyClass
与non-member函数结合使用:
MyClass<int> MCi(f<double>, f<bool>);
MCi.call_first(111); // Prints -> R f(const int&) [with R = double]
MCi.call_second(222); // Prints -> R f(const int&) [with R = bool]
查看现场演示 Here.