views:

187

answers:

2

I have the following function defined inside my linked list class. The declaration in the header file looks like this:

template <typename T>
class RingBuffer
{
  ...//stuff
  static BLink * NewLink (const T&); // allocator
};

BLink is a "link" class within the RingBuffer class. The following implementation code:

template <typename T>
RingBuffer<T>::BLink * RingBuffer<T>::NewLink( const T& t ) // this is line 114
{
  // create a new link in linked list
  ....
  ....
}

Is giving me this compile error:

./ringbuff.cpp:114: error: expected constructor, destructor, or type conversion before â*â token

I am stumped as to why it it needs an expected constructor, destructor, or type conversion before the return value.

A: 

compiler message is a little bit misleading. In basically signifies syntax/parsing error. Make sure the prototype is visible in compilation file, make sure you ring buffer is declared as template. and as other people have said, check the semi-column after class declaration.

aaa
+4  A: 

The problem here is that you are referring to a nested dependent type name (i.e. BLink is nested inside RingBuffer which is dependent on a template parameter)

You need to help your compiler a little in this case by stating that RingBuffer<T>::BLink is an actual type name. You do this by using the typename keyword.

template <typename T>
typename RingBuffer<T>::BLink * RingBuffer<T>::NewLink(const T& t)
{
  // ...
}

Explanation:

The compiler cannot know if RingBuffer<T>::BLink is a type name or a static member until the template parameter T is known. When the compiler parses your function template T is not known and the rule to solve the ambiguity is to default to "this is not a type name".

Another short example (blatantly copied from Scott Meyers' Effective C++):

template<typename C>
void print2nd(const C& container)
{
  C::const_iterator * x;
  …
}

This maybe illustrates the problem a little better as it's more compact. As already said it's not clear for the parser whether C::const_iterator is a type name or a static data member as it doesn't know what C is when it parses this part of the code (it may know at a later point in time when the template is actually instantiated). So to ease the compiler implementers' lives this ambiguity is resolved to "not a type name" and if the programmer wants to use a type name which is nested inside anything that is dependent on a template parameter he/she has to use the typename keyword in front of the name to let the compiler know that it should be treated as a type name.

Unfortunately there is an exception to that rule regarding nested dependent type names when using them inside a base class list or the base class identifier in a member initialization list.

template<typename T>
struct Base {
  struct Nested {
    Nested(int) {}
  };
};

template<typename T>
struct Derived : public Base<T>::Nested { // typename not allowed here
  Derived(int i)
    : Base<T>::Nested(i) // nor here
  {}
};

Btw: You should set your console client's charset to UTF-8, so you get ‘*’ instead of â*â.

Dirk D
thank you Dirk, can you maybe give a bit more explanation?
nmr
thanks for the UTF-8 tip
nmr
You're welcome. I just added more detail.
Dirk D
thanks for the very good explanation
nmr