我有自己的智能指针实现,现在我正在尝试解决通过指针调用成员函数的问题。我不提供任何类似于get()的函数(实际上,我提供了一个operator->,但我不想将其用于此目的)。
我的问题是: operator->*的签名和返回类型应该是什么样子的?
我有自己的智能指针实现,现在我正在尝试解决通过指针调用成员函数的问题。我不提供任何类似于get()的函数(实际上,我提供了一个operator->,但我不想将其用于此目的)。
我的问题是: operator->*的签名和返回类型应该是什么样子的?
为了完整起见,这里提供一个完整的、可编译的、最小化的示例,灵感来自于我链接的这篇论文,并通过剥离和小规模使用演示来引导您开始使用:
#include <memory>
#include <iostream>
#include <utility>
// Our example class on which we'll be calling our member function pointer (MFP)
struct Foo {
int bar() {
return 1337;
}
};
// Return value of operator->* that represents a pending member function call
template<typename C, typename MFP>
struct PMFC {
const std::unique_ptr<C> &ptr;
MFP pmf;
PMFC(const std::unique_ptr<C> &pPtr, MFP pPmf) : ptr(pPtr), pmf(pPmf) {}
// the 'decltype' expression evaluates to the return type of ((C*)->*)pmf
decltype((std::declval<C &>().*pmf)()) operator()() {
return (ptr.get()->*pmf)();
}
};
// The actual operator definition is now trivial
template<typename C, typename MFP>
PMFC<C, MFP> operator->*(const std::unique_ptr<C> &ptr, MFP pmf)
{
return PMFC<C, MFP>(ptr, pmf);
}
// And here's how you use it
int main()
{
std::unique_ptr<Foo> pObj(new Foo);
auto (Foo::*pFn)() = &Foo::bar;
std::cout << (pObj->*pFn)() << std::endl;
}
operator->*()
需要两个参数:
operator->*()
的简单“智能”指针:template <typename T>
class my_ptr
{
T* ptr;
public:
my_ptr(T* ptr): ptr(ptr) {}
template <typename R>
R& operator->*(R T::*mem) { return (this->ptr)->*mem; }
template <typename R, typename... Args>
struct callable;
template <typename R, typename... Args>
callable<R, Args...> operator->*(R (T::*mem)(Args...));
};
const
版本:这应该很简单,所以我省略了这些。基本上有两个版本:
callable
对象。 callable
将需要一个函数调用运算符,并适当地应用它。callable
类型:它将保存对对象和成员的指针,并在调用时应用它们:#include <utility>
template <typename T>
template <typename R, typename... Args>
struct my_ptr<T>::callable {
T* ptr;
R (T::*mem)(Args...);
callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {}
template <typename... A>
R operator()(A... args) const {
return (this->ptr->*this->mem)(std::forward<A>(args)...);
}
};
好的,这很直白。唯一棘手的地方在于函数调用运算符所使用的参数可能与成员指针的类型不同。上述代码通过简单地转发来处理这种情况。
上述callable
类型的工厂函数缺失了:
template <typename T>
template <typename R, typename... Args>
my_ptr<T>::callable<R, Args...> my_ptr<T>::operator->*(R (T::*mem)(Args...)) {
return callable<R, Args...>(this->ptr, mem);
}
好的,这就是全部了!这是使用C++11变长模板非常多的代码。手动敲出来以供C++03使用并不是我喜欢的事情。但好在,我认为这些运算符可以作为非成员函数。也就是说,它们可以在适当的命名空间中实现,该命名空间仅包含这些运算符和一个空标签类型,智能指针类型将从中继承。通过基本标记,运算符将通过ADL被发现,并适用于所有智能指针。例如,它们可以使用operator->()
来获取构造callable
所需的指针。
使用C++11,实际上很容易独立于任何特定的智能指针类型来实现operator->*()
的支持。下面的代码显示了实现和简单使用。它利用了这个版本只能基于ADL找到(您不应该有对它的using指令或声明)且智能指针可能实现了operator->()
的事实:代码使用这个函数来获取智能指针的指针。member_access
名称空间可能应该放在一个适当的头文件中,只需由其他智能指针简单地继承member_access::member_access_tag
(它可以是private
(!)基类,因为仍然会触发ADL来查看member_access
)。
#include <utility>
namespace member_access
{
struct member_access_tag {};
template <typename Ptr, typename R, typename T>
R& operator->*(Ptr ptr, R T::*mem) {
return ptr.operator->()->*mem;
}
template <typename R, typename T, typename... Args>
struct callable {
T* ptr;
R (T::*mem)(Args...);
callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {}
template <typename... A>
R operator()(A... args) const {
return (this->ptr->*this->mem)(std::forward<A>(args)...);
}
};
template <typename Ptr, typename R, typename T, typename... Args>
callable<R, T, Args...> operator->*(Ptr ptr, R (T::*mem)(Args...)) {
return callable<R, T, Args...>(ptr.operator->(), mem);
}
}
template <typename T>
class my_ptr
: private member_access::member_access_tag
{
T* ptr;
public:
my_ptr(T* ptr): ptr(ptr) {}
T* operator->() { return this->ptr; }
};
->*
运算符的重载需要两个参数:1.你的类的对象,2.成员指针。最简单的情况是,重载运算符应该是你的类的一个成员函数,接受一个指向成员的指针作为参数。例如,对于没有参数的成员函数指针,它应该是这样的:TYPE operator->*(void (YourClass::*mp)());
operator()
的意义)。最好使用另一个类来展示,这里有一个完整的例子:struct Caller {
void operator()() { cout << "caller"; }
};
struct A {
void f() { cout << "function f"; }
Caller operator->*(void (A::*mp)()) { return Caller(); }
};
int main() {
A a;
void (A::*mp)() = &A::f;
(a->*mp)();
return 0;
}
该代码将输出"caller"。在实际应用中,您需要使用模板来支持各种成员指针类型。更多详细信息请参见Scott Meyer的论文。
->*
和.*
”在C++11标准的第5.5节中有所描述。它们确实存在于C++中,即使它们并不常用。 - Michael Burr