I am implementing a smart pointer class template and I want to overload operator ->*
(even if it’s rarely done). I came across Scott Meyer’s article Implementing operator ->* for Smart Pointers. The article is from 1999 so I decided to try to adapt the code for C++ 14 (using parameter packs and perfect forwarding) and to improve it by adding support for pointer to data member—Meyer’s implementation only supports pointer to member functions—(which is the first of the two remaining problems of Meyer’s implementation that are listed at the section “Loose Ends” of his article and that he gives the reader as exercises to solve).
I ended up with the following code that seems to work quite fine. Now I have two questions:
- Is there anything that could be improved in my code?
- How to solve the second remaining problem listed at the section “Loose Ends” of Meyer’s article? (“You can’t use user-defined pointers-to-members. If someone has overloaded operator ->* to take objects that act like member pointers, you may want to support such ‘smart pointers to members’ in your smart pointer class. Unfortunately, you need traits classes to get the result type of such overloaded operator ->*.”)
Here is the code:
#include<iostream>
template<typename T>
struct Member_function_type_traits { };
template<typename O, typename... A, typename R>
struct Member_function_type_traits<R (O::*)(A...)> {
typedef O Object_T;
typedef R Return_T;
};
template<typename O, typename... A, typename R>
struct Member_function_type_traits<R (O::*)(A...) const> {
typedef O Object_T;
typedef R Return_T;
};
template<typename O, typename... A, typename R>
struct Member_function_type_traits<R (O::*)(A...) volatile> {
typedef O Object_T;
typedef R Return_T;
};
template<typename O, typename... A, typename R>
struct Member_function_type_traits<R (O::*)(A...) const volatile> {
typedef O Object_T;
typedef R Return_T;
};
template<typename T>
class Pending_member_function_call {
typedef typename Member_function_type_traits<T>::Object_T Object_T;
typedef typename Member_function_type_traits<T>::Return_T Return_T;
std::pair<Object_T*, T> operands;
public:
Pending_member_function_call(std::pair<Object_T*, T> opr): operands{opr} { }
template<typename... U>
Return_T operator ()(U&&... args) {
return (operands.first->*operands.second)(std::forward<U>(args)...);
}
};
template<typename T>
class Smart_pointer {
T* ptr;
public:
Smart_pointer(T* ptr): ptr{ptr} { }
~Smart_pointer() { delete ptr; }
// …
// operator ->* overload for pointers to data member
template<typename U, typename V>
V operator ->*(V U::* pdm) const {
return ptr->*pdm;
}
// operator ->* overload for pointers to member function
template<typename U>
Pending_member_function_call<U> operator ->*(U pmf) const {
return std::make_pair(ptr, pmf);
}
};
struct A {
int dm{5};
int mf(int x) { return 2 * x; }
};
int main() {
Smart_pointer<A> p{new A};
auto pdm = &A::dm;
auto pmf = &A::mf;
std::cout << "A::dm called: " << p->*pdm << '\n';
std::cout << "A::mf called: " << (p->*pmf)(8) << '\n';
return 0;
}
Smart_pointer
otherwise you are going to end up wiht a double delete. \$\endgroup\$unique_ptr
from the GNU C++ Library and chapter VII of Modern C++ Design on smart pointers by Andrei Alexandrescu. What sources did you use for your articles? \$\endgroup\$