Search code examples
c++c++11templatescode-duplication

template const / non const method


Suppose we have code like this:

template<class CALLBACK>
struct INIFile{
    INIFile(CALLBACK &processor) :
                    processor(processor     ){}

    bool process(){
        // lots of code here,
        // call processor
        processor(123);

        return true;
    }

    CALLBACK    &processor;
};


struct MyProcessor{
    void operator()(int val){
        // do something
    }
};

struct MyConstProcessor{
    void operator()(int val) const{ // !!!!!
        // do something
    }
};

int main(){
    MyProcessor p;
    INIFile<MyProcessor> ini(p);
    ini.process();

    // const case:
    MyConstProcessor cp;
    INIFile<MyConstProcessor> cini(cp);
    cini.process();
}

In both cases INIFile<>::process() will be a non const member function.

Is there an easy way to make process() a const member function if CALLBACK::operator() is const, without duplicate all logic in INIFile<>::process()?


Solution

  • Your problem is solved by doing the following:

    template<class CALLBACK>
    struct INIFile{
        INIFile(CALLBACK &processor) :
                        processor(processor){}
    
        template <class T>
        bool process_impl(T& processor) const {
            // deliberately shadow processor
            // reduce likelihood of using member processor, but can use other member variables
    
            // lots of code
            processor(123);
            return true;
        }
    
        bool process() const {
            return process_impl(const_cast<const CALLBACK&>(processor));
        }
        bool process() {
            return process_impl(processor);
        }
    
        CALLBACK&    processor;
    };
    

    This of course technically overloads process, but it has the exact same effect that you want. If the processor's call operator is not marked const, and you try to call process via a const reference or const copy of the object, you get a compilation error (unlike with your solution). That's because the const overload of process gets called, it adds the const to the processor which gets passed along, and then of course the call operator on the processor fails.

    However, if the callback does provide a const call operator, then either process call will do exactly the same thing. This effectively means that you can call process on a const copy of INIFile, which is equivalent to process being const.

    If the callback also overloads the call operator, then this implementation will forward along to whichever is correct, but you didn't specify that as a condition. The only thing to watch out for, is that process_impl should never access the member variable processor, because that member variable will always be mutable, i.e. the call will work even when it shouldn't (like in your solution). I shadow intentionally to try to prevent this. This isn't that beautiful but as an implementation detail it's not so bad, and it does remove the duplication.