'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:

  1. Use a single functor and overload the function call operator in an ad hoc fashion.
  2. Use two functor classes (one for const objects and another non-const ones) derived from a common base class.
  3. 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