Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two-phase lookup: is it possible to easily mix inheritence and templates

Intro: The C++ standards differentiates between symbols name which depend on template arguments, and names which don't, that's called two-phase name lookup (see here). Non-dependant names are resolved as soon as possible when you define your template. On the other hand, dependant names are only resolved at template instanciation.

Example:

template<class T> struct Base {
    typedef T type;
    static const int n = 3;
    virtual int f() = 0;
    int f(int x) { return x * 2; }
};

// doesn't compile!
template<class T> struct Derived : Base<T> {
    type field;         // The compiler doesn't know Base<T>::type yet!
    int f() { return n; } // the compiler doesn't know n yet, and f(int) is maksed!
};

Currently, what I do is define Derived like this:

template<class T> struct Derived : Base<T> {
    typedef Base<T> Parent;
    typedef typename Parent::type type; // correct but
    using Parent::n;                    // boring, long
    using Parent::f;                    // and harder to maintain
    type field;
    int f() { return n; }
};

For me one of the main goal of object-oriented programming is reduce code duplication; this kind of defeat the purpose...

Question: is there another way do define Derived by using some syntax I don't know of or a smart trick? I'd love something like this:

template<class T> struct Derived : Base<T> {
    using Base<T>::*; // I promise I won't do strange specializations of Base<T>
    type field;
    int f() { return n; }
};

Edit Clarification: maybe I wasn't specific enough. Imagine you have a about ten typedefs / fields / functions in Base, and tens of derived classes with less than 5 lines of specific code for each. This means that most of the code will consist of repeated typedefs and using clauses, I know there's no way to totally avoid that, but I'm looking to minimize this repetitive code.

Thanks for any ideas making this easier to write and maintain!

like image 245
Antoine Avatar asked Mar 20 '23 06:03

Antoine


2 Answers

T field;

That shouldn't be a problem; T is the template parameter itself, not a dependent name.

return n;

That is indeed a problem, since it's a dependent name and not known to be a member. The simplest solution is

return this->n;

Base<T>::n and Derived::n will also work, but I'd prefer not to duplicate the class names.

UPDATE

type field;

Unfortunately, there's no trick to access a dependent typename more simply than

typename Base<T>::type field;
like image 130
Mike Seymour Avatar answered Apr 26 '23 13:04

Mike Seymour


Just hear me out a little

#include <string>
#include <iostream>

template<class T> struct Base {
    typedef T type;
    static const int n = 3;
    virtual int f() = 0;
    int f(int x) { return x * 2; }
};

// does compile
template< class T, template<typename> class Base = Base > 
struct Derived : Base<T> 
{
    typename Base<T>::type field; 
    int f() 
    {
        field = 200;
        return n;
    }
    int f(int x)
    {
        return Base<T>::f(x);
    }
};

int main()
{
    Derived<int> bss;
    std::cout << bss.f() << std::endl;
    std::cout << bss.f(50) << std::endl;
    std::cout << bss.field << std::endl;

    return 0;
}
like image 24
Claudiordgz Avatar answered Apr 26 '23 13:04

Claudiordgz