tags:

views:

134

answers:

5

Is there any scope problem in this program?

#include<iostream>

using namespace std;

template<class Type>
class Base
{
   public:
   Type member;
   Base(Type param): member(param){

   }
};

template<class Type>
class Derived: public Base<Type>
{
    public:               
    Derived(Type param):Base<Type>(param){
     //                       ^
     //                       |_______  Not writing Type here gives error, why?
    }
    void display()
    {
        cout << member; /** ERROR HERE **/
    }
};

int main()
{
   Derived<int> p(5);
   p.display();
   return 0;
}

I get error 'member' was not declared in this scope. How to fix the problems?

+1  A: 

At the point where the compiler reads the template (not when it instanciates it), it cannot tell what Base<Type> is (it could be specialized), and therefore doesn't attempt to deduce it has a member member. You have to explicitely tell it: cout << this->Base<Type>::member;.

I think (check it, I'm not sure) that a using Base<Type>::member at class scope works too.

Alexandre C.
doesn't this->member also work? And if I remember correctly, this is not implemented the same in all compilers: gcc chokes on this example but msvc probably would not.
stijn
@stijn: MSVC is particularly laxist on name lookup... I got biten by this a incredible number of times.
Alexandre C.
MSVC doesn't implement two phase lookup, that is why it is incorrectly lenient on template name lookup. As a rule of thumb if gcc and MSVC disagree on whether a piece of code is legal or not gcc is most often right (Also MSVC got much much better in recent releases i.e. VS2008 and 2010).
Fabio Fracassi
+6  A: 

Your question is somewhat confusing. At first I thought you were asking about base<Type> in the member initialization list, then I thought you were asking about accessing member, then back to the first... Now I'm thinking you're asking both, so I'll answer both.


Not writing Type here gives error, why?

When you use a class template's name (my_class_templ), it refers to the template, which is not a type. In order to use it as a type, you need to provide template parameters (my_class_templ<int>, my_class_templ<T>). So wherever a type name is needed (and that includes the base class names in an initialization list), you need to provide template parameters.

You can omit the template parameter list for class templates names within the class template's definition. For example, a copy constructor can be declared as

 my_class_templ(const my_class_templ& rhs);

instead of

 my_class_templ<T>(const my_class_templ<T>& rhs);

This is just a little syntactic sugar, allowing your to type less.

However, outside of the class templates definition, you need to explicitly spell out all the template parameters. This is also true for derived classes:

my_dervied_class_templ(const my_derived_class_templ& rhs)
 : my_class_templ<T>(rhs)                          // need to spell out <T> here
{
}

I get error 'member' was not declared in this scope. How to fix the problems?

When your template is encountered first by the compiler, there's only its definition and no instantiations have been seen by the compiler yet. The compiler doesn't know whether, at a point of instantiation, there might be specializations of the template in scope or not. However, you could specialize your template for Base<T>::member to refer to something else or not to be defined entirely. (Say, a specialization Base<void> doesn't have a data member.) Therefore, the compiler must not speculate about the members of Base. Consequentially, they will not be looked up in Derived.

The result of this is that, if you need to refer to one of the members of Base, you need to tell the compiler that you expect a Base<T> to have such a member. This is done by fully qualifying its name: Base<Type>::member.

sbi
+1 you have explained it very well. :)
Prasoon Saurav
@Prasoon: So did you. `:)`
sbi
Actually both the base class specifier issue in the initializer list and the `member` issue have both the same origin. You *can* omit the template argument list and find the injected class name of `Base<Type>` instead (which is a perfectly well type name). The problem however is that `Base` is non-dependent, so cannot be looked up in the dependent base class. If he would say `:Derived::Base(param)` this would work perfectly well (on GCC starting with v4.5 only, since they did not properly implement injected-class-name lookup before)
Johannes Schaub - litb
@Johannes: Hmm understood the point. `:Derived::Base(param)` works on `g++` (4.5) and `CLang` .
Prasoon Saurav
@Johannes : BTW on MSVC++2010 everything works :D `:Derived::Base(param)`, `:Base(param)`, `:Base<Type>(param)`, `:Derived<Type>::Base<Type>(param)` all are equivalent. Furthermore even writing just `member` is fine. No need for its fully qualified name.
Prasoon Saurav
@Prasoon: That's because VC _still_ doesn't do proper two-phase lookup.
sbi
A: 
Derived(Type param):Base<Type>(param){ 

That Base<Type> is required because the base of Derived is Base<T>. There is nothing called Base.

void display()   
{   
        //cout << member; /** ERROR HERE **/ 
        cout << this->member;
        cout << this>Base<Type>::member;  
}

Alternatively having a using declaration in the scope of 'Derived' is also a valid technique.

Chubsdad
Please note my comment on @sbi's answer :)
Johannes Schaub - litb
@Johannes Schaub - litb: Sorry, but I don't think I understood it completely. The injected name of 'Base<Type>' is 'Base<Type>' or just 'Base'?
Chubsdad
@Chubsdad it's `Base`. `Base<Type>` is the injected-class-name followed by an argument list. See `14.6.1/1`. Try `struct A : std::vector<int> { A():vector() { } };`. On conforming implementations that works perfectly. Note that `typename` is not needed before dependent qualified type names used as a base-class in the class head or constructor initializer list.
Johannes Schaub - litb
+9  A: 

Not writing Type here gives error, why?

If you omit Type there is no way for the compiler to decide whether Base is a base class or is it a member of Derived. Specifying Type makes sure that Base is a template class [base class].

'member' was not declared in this scope

This is something to do with the rules for name lookup (dependent base classes).

C++03 [Section 14.6/8] says

When looking for the declaration of a name used in a template definition, the usual lookup rules (3.4.1, 3.4.2) are used for nondependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known (14.6.2).

Now Section 14.6.2/3 says

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

member is an unqualified name so the base class is not examined.

So you have two options.

  1. Use fully qualified name of Member i.e Base<Type>::member
  2. Use this->member.
Prasoon Saurav
Please note my comment on @sbi's answer :)
Johannes Schaub - litb
Yes thanks[....]
Prasoon Saurav
+2  A: 

The C++ standard requires a compiler to do a "two phase lookup" for templates. That is, they are trying to resolve all non-dependent names (names that don't depend on template parameters) in the first phase when the template is parsed and all the dependent names in the second phase when the template is instantiated.

If you don't qualify member it is treated as a non-dependent name and lookup fails in the first phase. You can solve this by prepending this-> to it. This makes member a dependent name and lookup is delayed until you actually instantiate the template.

sellibitze