Search code examples
c++c++11gccc++14mingw-w64

Universal member-function definition, instanceable from both 'const' & 'non-const' objects


Is there a way to define a member function that is both accessible from 'const' & 'non-const' objects?

I need this for my sList implementation of list class. In it I want to declare a function which will take as a parameter another function with either 'const' or 'non-const' pointer to sList and will call it for each list in the current sList structure.

Here is its declaration:

template <typename T>
struct sList
{
    sList(initializer_list<T>);

    inline void DoForEachList(auto pFunc)
    {
        for(auto p = this; p; p = p->pNext)
            pFunc(p);
    }

    ~sList();

    T dat;

    sList *pNext = nullptr;
};

I'm using auto pFunc because I want to eventually pass lambdas too. So now if I have a const object of this type and call from it 'DoForEachList' passing as an argument lambda function with 1 arg from type 'auto'. My compiler will fail with something like:

error: passing const sList<unsigned char> as this argument of void sList<T>::DoForEachList(auto:1) [with auto:1 = main()::<lambda(sList<unsigned char>*)>; T = unsigned char]' discards qualifiers [-fpermissive]

And the code calling DoForEachList:

void main()
{
    extern const sList<unsigned char> cvobj;
    cvobj.DoForEachList([] (auto pCurr) {/* Do something */});
}

Is there some way I can define the DoForEachList member function (or template of member function) like this:

template <typename T>
struct sList
{
    inline void DoForEachList(auto pFunc) auto //either 'const' or none
    {
        for(auto p = this; p; p = pNext->pNext)
            pFunc(p);
    }

    //...
};

Solution

  • You should use only the const member function and make the member variable mutable, which tells the compiler/you that the member does not influence the "has-been-changed" behaviour of the class. If this is not the case, rethink your design, because this is not quite right. mutable is more correct where e.g. a map's key needs to be modified in such a way that it does not influence the ordering in that map, but even then...

    Code example:

    struct Bla
    {
        void increment() const { i++; }
    private:
        mutable int i = 0;
    };
    
    int main()
    {
        const Bla bla;
        bla.increment();
    }
    

    Live demo here.