Search code examples
c++generic-programming

How to avoid repetition of the enclosing class type when auto and decltype() cannot be used


I recently discovered the auto and decltype() features of C++11, which are excellent, as they allow eliminating a lot of redundant type code. However, there are contexts where they cannot be used. One example I'm primarily asking about is if you want to declare a variable whose type uses the enclosing class type, either directly, or as a template argument, and you don't have an initialization expression (which would have allowed you to use auto). This is particularly undesirable if the enclosing class type is a template class with many template parameters. For example:

template<typename T1, typename T2, typename T3, typename T4, typename T5 > struct S {
    std::map<int,S<T1,T2,T3,T4,T5>*> m1;
    std::map<int,S<T1,T2,T3,T4,T5>*> m2;
    std::map<int,S<T1,T2,T3,T4,T5>*> m3;
    std::map<int,S<T1,T2,T3,T4,T5>*> m4;
    std::map<int,S<T1,T2,T3,T4,T5>*> m5;
};

A logical solution is to use a typedef:

template<typename T1, typename T2, typename T3, typename T4, typename T5 > struct S {
    typedef S<T1,T2,T3,T4,T5> ThisClass;
    std::map<int,ThisClass*> m1;
    std::map<int,ThisClass*> m2;
    std::map<int,ThisClass*> m3;
    std::map<int,ThisClass*> m4;
    std::map<int,ThisClass*> m5;
};

But it's still undesirable to have to declare a typedef that just repeats the enclosing class's type.

This can actually be solved if you're inside an instance method, by deducing the type of *this, although the necessary code is more verbose than I would like:

auto copy(void) {
    typename std::remove_reference<decltype(*this)>::type s = *this;
    // ... do stuff with s ...
    return s;
}

This solution does not work in class scope, because this is not allowed and is not meaningful outside of instance methods (the compiler complains "invalid use of ‘this’ at top level").

So, my question is, when you can't use auto or decltype(), what is the recommended solution for avoiding repetition of the enclosing class's type when you have to use it inside the class definition? Is a typedef the only option?


Solution

  • No need to repeat the template parameters if you are referring to the current instantiation.

    template<typename T1, typename T2, typename T3, typename T4, typename T5 > struct S {
        std::map<int,S*> m1;
        std::map<int,S*> m2;
        std::map<int,S*> m3;
        std::map<int,S*> m4;
        std::map<int,S*> m5;
    };
    

    The injected-class-name S refers to the current instantiation, in this case S<T1, T2, T3, T4, T5>.