4

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!

Antoine
  • 13,494
  • 6
  • 40
  • 52
  • 1
    What does string `typename Base::type T field;` mean? Should be `typename Base::type field;` or `T field;`, I think... – Constructor Mar 12 '14 at 18:37
  • There are 4 solutions to this problem: **1)** Use the prefix `Base::n`, **2)** Use the prefix `this->n`, **3)** Add a statement `using Base::n`, **4)** Use a global compiler switch that enables the permissive mode. The pros & cons and details of these solutions are described in https://stackoverflow.com/questions/50321788/a-better-way-to-avoid-public-member-invisibility-and-source-code-bloat-repetitio – George Robinson May 14 '18 at 14:14

3 Answers3

5
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;
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Thanks, I'm sorry I made a mistake in my example code! I meant a dependant name such as Base::type, I edited my question. The code is just an example - imagine you have several dependant types is ther a way to easily use them ? – Antoine Mar 12 '14 at 18:34
  • +1 for `this->` for fields. What about the more subtle `f` : without `using` name masking and bad things may occur. – Antoine Mar 12 '14 at 18:37
  • @Antoine: You mean if you want to specifically call the base-class `f`, not a member of the derived class that hides it? You'll have to specify `Base::f`; just as you would if there were no templates. – Mike Seymour Mar 12 '14 at 18:41
  • @Antoine What's the problem with `f()`? In your example, you don't need the `using Parent::f;` declaration because `f()` is being defined in `Derived`. Now, assuming `Base::f()` was not pure virtual, and you were not overriding it in `Derived`, if you wanted to call it within some member function of `Derived`, invoke it as `this->f();` [Example](http://coliru.stacked-crooked.com/a/dc628a97ed929738) – Praetorian Mar 12 '14 at 18:42
  • @Example: if I don't do `using Parent::f` wouldn't `f` be defined as a non virtual function? Which could screw me up at runtime. Also, isn't f(int) masked sometimes (not sure in this exact case, but I know I've had issues with overloaded funcs without `using`) – Antoine Mar 12 '14 at 18:49
1

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;
}
Claudiordgz
  • 3,023
  • 1
  • 21
  • 48
  • I don't see how parametrizing with `Base` helps, my question was about removing `using Base...`/`typedef Base...`. – Antoine Mar 12 '14 at 18:56
  • I don't understand, I though you wanted to define the Base class type when instantiating the Derived class. I don't think the question is clear enough if that's not the case. With this, your derived field depends on your Base field, and it gets instantiated on the creation of Derived. Is this not what you are looking for? – Claudiordgz Mar 12 '14 at 19:02
  • Excuse me if I wasn't clear enough! I Clarified in the question. +1 for your idea though. – Antoine Mar 12 '14 at 21:37
  • I'm sorry for the confusion. In your case maybe you could try double inheritance, one from the struct of non-generics that you don't want to repeat in your code and another from the Base. It really depends on the list of things you want in the Base used in the derived ones. – Claudiordgz Mar 12 '14 at 22:01
1

This doesn't answer the question, but if you do allow specializations of Base (which... you really have to) then the behavior can get really weird.

Like consider this 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; }
};

typedef float type;
static const int n = 5;

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

It might not be intuitive, but at least the code is predictable. Derived::field is always a float and Derived::f() always returns 5.

If we somehow tricked the compiler into using every member of Base, then specializing Base in a weird way would cause Derived to behave in a very hard to determine way when it should be erroring out.

QuestionC
  • 10,006
  • 4
  • 26
  • 44
  • The purpose of templates is to allow the behavior you are removing. Yes, it allows " Derived to behave in a very hard to determine way" but it is the price of metaprogramming. There are other ways of limiting the developer while still using generics. – Claudiordgz Mar 12 '14 at 21:20
  • I know... I guess what i'm really after is some kind of "interface" to which all Base specializations must conform (a type typedef, value n, etc.) and which could be in the lookup scope by default. Maybe C++ concepts solve this? – Antoine Mar 12 '14 at 21:24