'Overloading the pointer-to-member function operator. Attempting to resolving the const overloaded version isn't compiling
I'm currently doing practicing on the ->* operator with the intent to write a smart pointer. I've done the basics on how it works. For this example I want to use templates when using operator->*(), so I can use member functions for a variety of return and paramater types.
Below is a simple example of my objective
Functor.h
#pragma once
#include "RecordCard.h"
template <class OBJECT_TYPE, typename POINTER_TO_MEMBER>
class Functor{
public:
Functor(OBJECT_TYPE* pObj, POINTER_TO_MEMBER pMF):m_pObj(pObj),m_pMF(pMF){}
template <typename RETURN_TYPE>
RETURN_TYPE operator() const
{
return (m_pObj->*m_pMF)();
}
template <typename PARAM_TYPE>
void operator()(PARAM_TYPE param)
{
(m_pObj->*m_pMF)(param);
}
private:
OBJECT_TYPE* m_pObj;
POINTER_TO_MEMBER m_pMF;
};
RecordCard.h
#pragma once
#include "Functor.h"
#include <string>
template <class T, typename U>
class Functor;
class RecordCard{
public:
RecordCard(){}
void SetName(std::string);
void SetAge(unsigned int);
void SetActiveStatus(bool);
std::string GetName() const;
unsigned int GetAge() const;
bool GetActiveStatus() const;
// Other methods of the class
template <typename T>
Functor<RecordCard,T> operator->*(T pmf)
{
return Functor<RecordCard,T>(this, pmf);
}
template <typename T, typename U = T (RecordCard::*)() const>
const Functor<RecordCard,U> operator->*(U pmf) const
{
return Functor<RecordCard,U>(this, pmf);
}
private:
std::string m_szName;
unsigned int m_nAge;
bool m_bActive;
};
RecordCard.cpp
// Method definitions here
// RecordCard::operator->* definition removed from her
// and placed in the header file. Because to otherwise
// causes the linker to complain.
Now the problem lies in my main. Main.cpp
#include "RecordCard.h"
#include "Functor.h"
int main()
{
RecordCard mycard;
(mycard->*&RecordCard::SetAge)(30); // Works Okay
(mycard->*&RecordCard::GetAge)(); Error??
return 0;
}
The two complaints the compiler are giving me are:
Unable to find a matching signature for RecordCard::GetAge() const.
Unable to create an instance of Functor<RecordCard, int (RecordCard::*)()>
The reason why it's happening it's not calling operator->*() const.
Without using lambdas or std::function, those are for future excercises. How can resolve this so it works correctly.
Many thanks.
Solution 1:[1]
You need to change this:
void operator(PARAM_TYPE param)
To this instead:
void operator()(PARAM_TYPE param)
On a side note, in the 1st operator()
, you can get rid of the template if you use auto
for the return type:
auto operator() const
{
return (m_pObj->*m_pMF)();
}
Solution 2:[2]
I realised where I went wrong and kicked myself when I found out what was. The problem is that I overlooked what differing types of the this pointer between the const qualified and non-const objects
Case in point:
#include <iostream>
#include <typeinfo>
class Foo{
public:
void describe_my_type() const
{
std::cout << typeid(this).name << std::endl;
}
};
int main()
{
Foo foo1;
const Foo foo2;
foo1.describe_my_type(); // prints Foo*
foo2.describe_my_type(); // prints const Foo*
return 0;
}
Hence I came up with three approaches:
- Use a single functor and overload the function call operator in an ad hoc fashion.
- Use two functor classes (one for const objects and another non-const ones) derived from a common base class.
- Use a wrapper class and let that handle the pointer to member operator and the functor
What all three have in common:
- The functor is an internal class and itself is public of the outer class
- The functor grants friendship to its outer class to prohibit instantion for outsiders.
- The constructors are private.
- An addition header describing the traits of class methods
MethodTraits.h
// Base struct which has the common traits to avoid repetition
template <class O>
struct MethodTraitsBase{
using OBJECT = O;
};
// Primary template
template <typename T>
struct MethodTraits;
// Template specialisation
template <typename R, class O, typename... A>
struct MethodTraits<R(O::*)(A...)>
: MethodTraitsBase<O>{
};
template <typename R, class O, typename... A>
struct MethodTraits<R(O::*)(A...) const>
: MethodTraitsBase<O>{
};
template <typename R, class O, typename... A>
struct MethodTraits<R(O::*)(A...) volatile>
: MethodTraitsBase<O>{
};
template <typename R, class O, typename... A>
struct MethodTraits<R(O::*)(A...) const volatile>
: MethodTraitsBase<O>{
};
APPROACH ONE: Single functor (RecordCard::Functor)
#include "MethodTraits.h"
class RecordCard{
public:
// As before
template <typename T>
class Functor{
friend class RecordCard;
typedef typename MethodTraits::OBJECT OBJECT_T;
public:
template <typename... U>
auto operator() (U&& ...args)
{
return (m_pObj->*m_pmf) (std::forward<U>(args)...);
}
template <typename... V>
auto operator() (V&& ...args) const
{
if (m_pmf)
return (m_pObj->*m_pmf) (std::forward<V>(args)...);
else
return (m_pcObj->*m_pmf) (std::forward<V>(args)...);
}
private:
Functor(OBJECT_T* pObj, T pmf)
: m_pObj(pObj), m_pmf(pmf), m_pcObj(nullptr)
{}
Functor(const OBJECT_T* pcObj, T pmf)
: m_pObj(nullptr), m_pmf(pmf), m_pcObj(pcObj)
{}
// deleted to prevent unauthorised crfeations
// copies and transfers
Functor() = delete;
Functor(const Functor&) = delete;
Functor& operator=(const Functor&) = delete;
Functor& operator=(Functor&&) = delete;
// C++17 this can be marked as delete too due to copy elision
// C++11/14 this needs unmarked as it invokes move semantics
Functor(Functor&&);
OBJECT_T* m_pObj;
const OBJECT_T* m_pcObj;
T m_pmf;
};
template <typename W>
Functor<W> operator->*(W pmf)
{
return Functor<W>(this, pmf);
}
template <typename X>
Functor<X> operator->*(X pmf) const
{
return Functor<X>(this, pmf);
}
private:
// as before
};
APPROACH TWO: Two functors derived from a common base type (RecordCard::FunctorBase)
class RecordCard{
public:
// As before
template <typename T>
class FunctorBase{
// All members protected access
protected:
typedef typename MethodTraits::OBJECT OBJECT_T;
FunctorBase(T pmf)
: m_pmf(pmf)
{}
// Delete other constructors except the move ctor
T m_pmf;
};
template <typename T>
class NonConstFunctor : public FunctorBase<T>{
friend class RecordCard;
public:
template <typename... U>
auto operator() (U&& ...args)
{
return (m_pObj->*m_pmf) (std::forward<U>(args)...);
}
private:
Functor(OBJECT_T* pObj, T pmf)
: FunctorBase(pmf), m_pObj(pObj)
{}
{}
// No need to mark ctors as deleted as base class does that
// Therefore compiler will complain
OBJECT_T* m_pObj;
};
template <typename T>
class NonConstFunctor : public FunctorBase<T>{
friend class RecordCard;
public:
template <typename... V>
auto operator() (V&& ...args) const
{
return (m_pObj->*m_pmf) (std::forward<V>(args)...);
}
private:
Functor(const OBJECT_T* pcObj, T pmf)
: FunctorBase(pmf), m_pcObj(pObj)
{}
{}
const OBJECT_T* m_pcObj;
};
template <typename W>
NonConstFunctor<W> operator->*(W pmf)
{
return NonConstFunctor<W>(this, pmf);
}
template <typename X>
ConstFunctor<X> operator->*(X pmf) const
{
return ConstFunctor<X>(this, pmf);
}
private:
// as before
};
APPROACH THREE: A wrapper class and a single functor
#include "MethodTraits.h"
template <class T>
class PointerWrapper{
public:
PointerWrapper(T* ptr)
: m_ptr(ptr)
{}
template <typename U>
class Functor{
friend class PointerWrapper<T>;
typedef typename MethodTraits::OBJECT OBJECT_T;
public:
// code to overload the function call operator
private:
// Two paramaters ctor
// Deleted ctors
// Two members
};
template <typename X>
Functor<X> operator->*(X pmf) const
{
return Functor<X>(m_ptr, pmf);
}
private:
T* m_ptr
};
It could implemented as such:
RecordCard rc;
PointerWrapper<RecordCard> wrapper(&rc);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Remy Lebeau |
Solution 2 | SparkieMember |