Search code examples
c++c++11forward-declarationdecltypepimpl-idiom

get return type from a function of a class that was forward-ed declaraion


Is it possible to get return type UglyIterator<> of a function of a class B::f which is forward declaration?

Example

MyArray is a class that acts like std::vector.
Its begin() and end() function return a ugly type.

template<class T> class MyArray{
     UglyIterator<Protocol1,Protocol2,SafetyFlag,brabrabra> begin(){
         //some code
     }
     //... other functions ....
};

B has MyArray<int> as a value field.
With the magic of auto-keyword, B can pretend to be a neat class.

#include "MyArray.h"
class B{  //just a bundle of data
    MyArray<int> bField;
    public: auto f(){  //<--- neat     
        return bField.begin();
    }
    //... other fields  ...
};

Manager is a manager of B and do other things.

#include "B.h"
class Manager{  //just a bundle of data
    decltype(&B::f) mField;  //I can cache it, so neat!
    //^ actually it is "UglyIterator<Protocol1,Protocol2,SafetyFlag,brabrabra>"
    //... other functions/fields ...
};

As project grow, I noticed that Manager.h was included in many files, and MyArray's code changed very often.

To reduce compile time, I decided to forward declaration at Manager.
I changed mField to mFieldPtr, but I get compile error :-

class B;
class Manager{  
    std::unique_ptr<std::result_of<decltype(&B::f)>::type> mFieldPtr; 
    //^ should compile error (can not recognize "B::f")
    //... other functions ...
};

How to get the return type decltype(&B::f) elegantly?

My workaround

Create a new file B_TopHeader.h.

using B_F_returnType = UglyIterator<Protocol1,Protocol2,SafetyFlag,brabrabra>;
//^ the type "UglyIterator" also need another forward declaration    

Then let the Manager #include B_TopHeader.h instead :-

#include "B_TopHeader.h"
class Manager{  
    std::unique_ptr< B_F_returnType > mFieldPtr;
    //... other functions ...
};

However, I think it is not elegant. It seems to be a hack.
I have to forward the return type manually.


Solution

  • You may use Pimpl idiom to hide the dependency, something like:

    class Manager
    {
    public:
        ~Manager() noexcept; // you certainly have also to handle copy/move
    
        // Stuff using mFieldPtr, but which doesn't return it.
    private:
        std::unique_ptr<struct Impl> mImpl;
    };
    

    And in cpp

    #include "Manager.h"
    #include "B.h"
    
    struct Manager::Impl
    {
        // Implementation using mField
    
        decltype(&B::f) mField;
    };
    
    
    Manager::~Manager() noexcept = default;
    
    // Forward methods of `Manager` to `Impl`.