operator->*() 有两个参数:
- 它正在操作的对象。
- 要应用的成员指针。
如果成员指针只是对数据成员的访问,结果很简单:您可以只返回对该成员的引用。如果它是一个函数,那么事情就有点复杂了:成员访问运算符需要返回一个可调用对象。可调用对象接受适当数量的参数并返回成员指针的类型。
我意识到最初的问题是用 c++03 标记的,但是做一个“正确的”C++03 实现是一个相当冗长的打字练习:你必须在下面的代码中通过可变参数模板方便地完成什么对于每个数量的参数。因此,这段代码使用 C++11 主要是为了更清楚地显示需要什么并避免进行打字练习。
这是一个定义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 并不是我真正喜欢的东西。从好的方面来说,我认为这些运算符可以是非成员函数。也就是说,它们可以在一个合适的命名空间中实现,该命名空间只包含这些运算符 using 和一个智能指针类型将从其继承的空标记类型。以 tag-tag 为基础,运算符将通过 ADL 找到并适用于所有智能指针。例如,他们可以使用operator->() 来获取构造callable 所需的指针。
使用 C++11 实现对 operator->*() 的支持实际上是相当直接的,独立于任何特定的智能指针类型。下面的代码显示了实现和简单的使用。它利用了这个版本只能基于 ADL 找到(你不应该有一个 using 指令或声明)并且智能指针可能实现 operator->() 的事实:代码使用这个函数来获取智能指针的指针。 member_access 命名空间可能应该进入一个合适的标头,该标头仅包含在其他智能指针中,然后仅从 member_access::member_acccess_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; }
};