Two-phase search: is it easy to mix inheritance and patterns

Introduction: . C ++ standards distinguish between character names that depend on template arguments and names that do not have a name called a two-phase name (see here ). Independent names are resolved as soon as possible when you define your template. Dependent names, on the other hand, are only resolved when the template is initialized.

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!
};

I am currently defining Derivedas follows:

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 goals of object-oriented programming is to reduce code duplication; this kind of hitting target ...

: Derived, - , ? - :

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; }
};

. : , . , typedefs/fields/functions Base 5 . , typedefs using , , , .

, !

+4
3
T field;

; T , .

return n;

, .

return this->n;

Base<T>::n Derived::n , .

UPDATE

type field;

, ,

typename Base<T>::type field;
+5

.

#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;
}
+1

, Base (... ), .

...

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

typedef float type;
static const int n = 5;

template<class T> struct Derived : Base<T> {
  type field;
  int f() { return n; }
};

, , , . Derived:: field float, Derived:: f() 5.

If we somehow tricked the compiler into using each member of the base, then specializing Base in a weird way would make Derived very difficult to determine how it should be a mistake.

+1
source

Source: https://habr.com/ru/post/1531406/


All Articles