Dependent Base Classes

without comments

Originally, this post was to be about the wonderful new C++ swizzle operators I’ve added to GLGeom, but due to a gcc compilation issue with the swizzle operations delaying that, I thought I might post on that error instead.

Below is a chunk of code that does NOT compile on gcc 4.6.1 but did work fine on Visual Studio 2010 (cl.exe 16.00.30319.01).

The Code

I’ll show the code first in case you want to try to track down the error on your own…

template <typename Type, typename Class, int N, int E0, int E1, int E2, int E3>
struct swizzle_base
{
    typedef swizzle_base<Type,Class,N,E0,E1,E2,E3> base;
 
    swizzle_base& operator= (const Class& that)
    {
        static const int offset_dst[4] = { E0, E1, E2, E3 };
 
        Type t[N];
        for (int i = 0; i < N; ++i)
            t[i] = that[i];
        for (int i = 0; i < N; ++i)
            e[offset_dst[i]] = t[i];
 
        return *this;
    }
 
    swizzle_base& operator= (const Type& t)
    {
        static const int offset_dst[4] = { E0, E1, E2, E3 };
 
        for (int i = 0; i < N; ++i)
            e[offset_dst[i]] = t;
 
        return *this;
    }
 
    Type    e[N];
};
 
//===========================================================================//
 
template <typename T, typename P, int E0, int E1>
struct swizzle2 : public swizzle_base<T,P,2,E0,E1,0,0>
{
    using base::operator=;
    operator P () { return P(e[E0], e[E1]); }
};

See the error? There’s no way I would have :)

 

Spoilers ahead if you’re trying to work out the error on your own…

 

What GCC Complains About

The two complaints are as follows:

  • error: ‘base’ has not been declared
  • error: ‘e’ was not declared in this scope

It seems that both “base” and “e” are effectively not known, even though they are declared in the parent class. The parent class however, is a template, so that introduces a huge set of potential reasons for why it’s not so simple for the compiler to figure out.

The Fix

Two minutes on the FreeNode #c++ IRC channel got me a fix to the issue:

  • Type out the base class name fully rather than using the base typedef
  • Explicitly use “this->e” rather than “e” to inform GCC this is a member variable
template <typename T, typename P, int E0, int E1>
struct swizzle2 : public swizzle_base<T,P,2,E0,E1,0,0>
{
    using swizzle_base<T,P,2,E0,E1,0,0>::operator=;
    operator P () { return P(this->e[E0], this->e[E1]); }
};

But Why?

Here’s the sad anticlimax: I’m not exactly sure.

Internet searches for “dependent base classes” turn up some interesting threads like this one on stackoverflow, but I still could not find a concrete answer for why a “conforming compiler” needs the more verbose and explicit information. To flip the question around: what’s an example of where VS 2010 will generate incorrect code or parse something incorrectly because it is able to determine that “base” and “e” are in the dependent base class?

It seems to me – and I’m not a compiler writer – that the compiler could figure this out without causing any issue elsewhere (I’m likely wrong – as I said: not a compiler writer…or, for that matter, a C++ language spec stickler).

Let’s say the compiler keeps the templates “full unevaluated” until the time of instantiation. If the compiler can parse that swizzle2 is a template class deriving from swizzle_base before it parses any of the members of swizzle2, then it can fully evaluate swizzle_base with real template parameters and realize the concrete instance has a member “e” and a typedef “base”. It then moves on to the derived class swizzle2′s internals and it knows about “e” and “base”: thus, no problem. I feel like this is somewhat self-evident: if you know and substitute in the concrete parameters to swizzle2 then the above code is valid C++.

Therefore, I have to assume the compiler is not allowed to simply “skip over” the class internals before evaluating the parent class template. I’ll assume the compiler stores it in some partially evaluated form. It knows swizzle2 has a base class but – what with partial specialization, etc. – it probably can assume next to nothing about the parent class. Therefore, the derived class needs to explicitly, or at least somewhat explicitly, let the compiler know in the derived class about anything it will be using from the parent class. It’s not willing to simply assume an unknown token is something coming from the parent class.

As an example, suppose swizzle_base did have a partial specialization and that did not define any member called “e”. But there was a global variable named “e”. That means in one instantiation e refers to a member and in another it refers to a global. Confusing certainly, but I can’t think of why that’d necessarily be invalid. Now when you throw the “this->” in front of the “e” that case is no longer possible. It has to be coming from the object itself and not a global.

That sort of makes sense to me.

But the question still irks me: would a compiler that waits until the point of instantiation to evaluate all a template be a C++11 conformant compiler? And if efficiency is a concern, then why not make two internal pools in the compiler: templates that can be partially evaluated in advance and those that “seem to need more information” that get evaluated at the point of instantiation?

I truly like C++ as a language, but man, little syntactical messes like this really do there best to make sure I never refer to it as an elegant language :)

By the way, anyone have any more informed knowledge on why GCC can’t compile the original code and remain a “conformant” C++ compiler?

 

Leave a Reply