13

I am going over the WildMagic 5 engine (www.geometrictools.com) where the Vector<> class is inheriting from a Tuple<> class which has an array of a particular size, named mTuple[] (set by a template parameter). So far so good, nothing special. In the Vector class however, I see the following:

protected:
    using Tuple<4,Real>::mTuple; 

Now I know that the using keyword is used for inheriting overloaded methods properly. In this case, I always assumed the variable was available to the derived class without typing the above code. Is the above necessary? Or is it just there to make things clearer?

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
Samaursa
  • 16,527
  • 21
  • 89
  • 160

3 Answers3

9

Generic programming is slightly different from object oriented programming.

Your mTuple is an example of a nondependent name. As far as the compiler is concerned, at the time the template definition is processed the compiler doesn't know that the class template has inherited a data member named mTuple. It might be obvious to you, but it is not obvious to the compiler. At this stage, the compiler is oblivious to the obvious.

If the methods of the derived class template wish to use some member of the parent class template, the compiler needs to be explicitly told to do so. Hence the using.

Edit

The above was a bit terse. It is important to remember that those class templates are not classes. They are templates that eventually define a class. Up until the moment that the class template is used to define a class that class template isn't quite real. More importantly, for a class template that inherits from some other class template, that inheritance isn't quite real. The compiler doesn't know about that inheritance unless it is explicitly told about it. That's why you will see derived class templates import the parent class's members via using ParentClass<Type>::member (for example).

Edit #2

Marshall Cline discusses this topic in his C++-FAQ at http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19

Edit #3

(Per request) Just because some code compiles on your compiler does not mean it compiles on every compiler (for the same language). Compiler vendors add their own 'features' to a language, sometimes very intentionally, sometimes just because the vendors themselves goofed up, and sometimes because the standard itself is buggy. This issue of not-quite-standard compilers has been a problem for a long time, with many languages. The problem apparently is quite rampant when it comes to generic programming.

You can do everything right (or so you think): Turn on all standard warnings and then some, run your code through some commercial code analyzer, and you still might not have portable code.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • But in my implementation I did not do that and everything compiles fine (maybe I understood your explanation wrong). It is when I looked at WM5 engine's implementation for reference that I saw this. – Samaursa Jun 14 '11 at 00:52
  • Everything compiles fine for you precisely because the authors of `Tuple` did the right thing. – David Hammen Jun 14 '11 at 01:24
  • The author of Tuple is me :)... like I said (I may not have been clear though), I wrote my own Vector and Tuple classes and wanted to compare with WM5's implementation and that is when I noticed this. I never had to type `using someVariable` for a derived class (I am not new to C++... been inheriting classes for quite some time! :) and thus was curious why this was needed especially since my version without `using` keyword compiles, runs and tests perfectly fine. Also, I am not sure how an `author of tuple`, as you put it, has anything to do with the derived Vector class. – Samaursa Jun 14 '11 at 03:35
  • If your Vector class template does not use any members of your Tuple class template then there is no need for such a `using` statement. If Vector does access members of Tuple but does so via `this->member` there also is no need for such a `using` statement. On the other hand, if Vector does access members of Tuple without any qualification, you have broken code. If your broken code compiles OK and runs as expected, you have a broken compiler (and thus you don't know you have broken code). – David Hammen Jun 14 '11 at 06:59
  • @David: Ah! Now that is something I didn't know and thank you for pointing it out. So if I understood correctly, the MSVC compiler is doing more than it should and my code may break on another standard compliant compiler (If you edit your answer a bit further with your comment above, I will mark this as the correct answer). – Samaursa Jun 14 '11 at 14:01
  • Selected as the answer. For future reference, please also see Johannes answer (http://stackoverflow.com/questions/6337127/using-keyword-for-base-class-variable/6339291#6339291) – Samaursa Jun 14 '11 at 16:42
8

What @David has been saying, but wasn't apparently understood by some. So I believe an example is in order:

template<typename T>
struct A { int a; };

template<typename T>
struct B : A<T> {
   void f() {
     a = 0; // #1
     this->a = 0; // #2
     B::a = 0; // #3
   }
   // using A<T>::a;
};

template<>
struct A<float> { }; // surprise!

#1 Will cause an error, because in a template, dependent base classes are not looked into by unqualified lookup. This is an important concept in C++. If there is a global a visible, then that a will be used by a = 0 - a member declared in a dependent base class will never hide that global a. For telling the compiler to look into dependent base classes, you have to qualify your name. So this->a works fine, as does B::a.

If you put a using A<T>::a, you tell the compiler to declare a member name a in the scope of B. Then a = 0 will find the a directly in B. That is an acceptable fix too.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • OK, so I learned something new today. But the questioner says he wrote his own version without the `using` declaration and it worked... – Nemo Jun 14 '11 at 05:11
  • @Nemo some compilers may not be up to sufficient strict Standard compliance. He may also not in his particular code actually need the using declaration, because everything may already use qualified name lookup. I don't know whether he *needs* that declaration in his code, because he's not given any more insight into it. – Johannes Schaub - litb Jun 14 '11 at 05:15
  • Thanks for your explanation. That is indeed something brand new I learned today. As far as the classes go, it is as simple as it can get. A Tuple<> template class with a protected member array that is being inherited by a Vector<> template class with no member variables. It compiles on Visual C++ 2008 (don't remember the compiler version). – Samaursa Jun 14 '11 at 13:59
  • Ok, one more clarification if you can (rather than starting a new question). Is this also required for non-template classes? – Samaursa Jun 14 '11 at 16:44
1

Normally, you can use a declaration like this to increase the access of an inherited member. Say from protected to public.

But you cannot use this syntax to restrict access. And you cannot use it on a member declared private.

So the only time this could do anything is if Vector inherits from Tuple<> like so:

class Vector4 : private Tuple<4,Real>
{ ... }

In that case, this using declaration would make mTuple protected instead of private.

In all other cases, it will do nothing, I believe.

[edit]

And my belief was wrong where template base classes are concerned. See the answers from Johannes and David.

Community
  • 1
  • 1
Nemo
  • 70,042
  • 10
  • 116
  • 153