1

I have for following Situation:

template <class K> class A
{
public:
    int a;
};

class B
{
public:
    virtual void DoSomething() = 0;
};


template <class K> class C : public A<K>, public B
{
public:
    virtual void DoSomething()
    {
        a = 3;      // ***
    }
};

Now this works on MSVC, however gcc tells me for the line with the 3 stars : "error: 'a' was not declared in this scope". I figured out that I can replace the line with

A::a = 3;

and it works on gcc (well mingw) as well. Do we have to add the original class name all the time to be standard conform? I thought, I only have to add it if names collide otherwise.

I'm using mingw32 (gcc) 4.8.1.

marc40000
  • 3,167
  • 9
  • 41
  • 63

1 Answers1

2

Now it makes sense...

GCC is right, and MSVC is wrong. It should not work because a is template dependent but the expression a; is not.

That is, the meaning you intend of a depends on a template argument (in your case K), but the expression that contains it does not. And since the compiler cannot be sure that the instantiation of A<K> will have a member named a, it simple assumes it does not.

There are several easy solutions, all of them involve using a template dependent expression so resolve a:

this->a = 3; //my favourite
A<K>::a = 3; //you found this one

EXAMPLE:

Let's see an example of why it should not work:

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

int a; //global variable to make things more interesting

template <typename K> struct B : A<K>
{
    void foo()
    {
        a = 3; //does it refer to A<K>::a or ::a?
    }
};

//template specialization of A<int>
template <> struct A<int>
{
};

B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?

Also, you can make the opposite situation:

template <typename K> struct A
{
};

int a; //global variable to make things more interesting

template <typename K> struct B : A<K>
{
    void foo()
    {
        a = 3; //does it refer to A<K>::a or ::a?
    }
};

//template specialization of A<int>
template <> struct A<int>
{
    int a; //now the member variable is in the specialization
};

B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?

As you can see, the standard C++ behavior is there for you protection, so that the code you write in a template will always refer -more or less- to the same things:

  • a: will be always the non template (global in the example) variable, if available. If not, it is a compiler error.
  • this->a: will be always the member variable, if available. If not, a compiler error.
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Hmm. Cool that you recognize the issue. :) Could you elaborate on the explanation why the Compiler cannot be sure that an instaniation of A will have a member named a? Looking at the definition of template A, all instantiations will have a member a. No? – marc40000 Nov 22 '13 at 17:32
  • 1
    @marc40000: Sure! Added. You are forgetting template specializations, those can be arbitrarily different from the unspecialized one. – rodrigo Nov 22 '13 at 17:44
  • Thanks for editing :) Is it really for my protection? The same situation in the non-template case allows me to just use a instead of A::a. Why is it different here? – marc40000 Nov 22 '13 at 17:50
  • 1
    @marc40000: because in the non-template case what `a` means is known when the compiler parses the function code. But in the template case, the compiler doesn't know what the members inherited from `A` will be: there may be an `a` or there may not. The fact that the definition of the `template class A` is available means nothing because of the possibility of template specializations. The actual members of `A` will not be known until the instantiation of the templates, but then only template dependent expression will be re-interpreted. See the examples: what behavior would you prefer? – rodrigo Nov 22 '13 at 17:54
  • So, is it possible to have an A that has a member named a and an A that does not have a member named a at the same time by using template specialization? – marc40000 Nov 22 '13 at 18:07
  • 1
    @marc40000: Exactly! Look up SFINAE for really cool uses of this feature. – rodrigo Nov 22 '13 at 18:09