views:

941

answers:

3

While working with some templates and writing myself a basic container class with iterators, I found myself needing to move the body of member functions from a template class into a separate file to conform to style guidelines. However, I've run into an interesting compile error:

runtimearray.cpp:17: error: expected constructor, destructor, or type conversion before '&' token runtimearray.cpp:24: error: expected constructor, destructor, or type conversion before '&' token runtimearray.cpp:32: error: expected constructor, destructor, or type conversion before '&' token runtimearray.cpp:39: error: expected constructor, destructor, or type conversion before '&' token runtimearray.cpp:85: error: expected constructor, destructor, or type conversion before 'RuntimeArray' runtimearray.cpp:91: error: expected constructor, destructor, or type conversion before 'RuntimeArray'

runtimearray.h:

#ifndef RUNTIMEARRAY_H_
#define RUNTIMEARRAY_H_

template<typename T>
class RuntimeArray
{
 public:
  class Iterator
  {
    friend class RuntimeArray;
   public:
    Iterator(const Iterator& other);

    T& operator*();
    Iterator& operator++();
    Iterator& operator++(int);
    Iterator& operator--();
    Iterator& operator--(int);
    bool operator==(Iterator other);
    bool operator!=(Iterator other);

   private:
    Iterator(T* location);

    T* value_;
  };

  RuntimeArray(int size);
  ~RuntimeArray();

  T& operator[](int index);

  Iterator Begin();
  Iterator End();

 private:
  int size_;
  T* contents_;
};

#endif  // RUNTIMEARRAY_H_

runtimearray.cpp:

#include "runtimearray.h"

template<typename T>
RuntimeArray<T>::Iterator::Iterator(const Iterator& other)
    : value_(other.value_)
{
}

template<typename T>
T& RuntimeArray<T>::Iterator::operator*()
{
  return *value_;
}

template<typename T>
RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator++()
{
  ++value_;
  return *this;
}

template<typename T>
RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator++(int)
{
  Iterator old = *this;
  ++value_;
  return old;
}

template<typename T>
RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator--()
{
  --value_;
  return *this;
}

template<typename T>
RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator--(int)
{
  Iterator old = *this;
  --value_;
  return old;
}

template<typename T>
bool RuntimeArray<T>::Iterator::operator==(Iterator other)
{
  return value_ == other.value_;
}

template<typename T>
bool RuntimeArray<T>::Iterator::operator!=(Iterator other)
{
  return value_ != other.value_;
}

template<typename T>
RuntimeArray<T>::Iterator::Iterator(T* location)
    : value_(location)
{
}

template<typename T>
RuntimeArray<T>::RuntimeArray(int size)
    : size_(size),
      contents_(new T[size])
{
}

template<typename T>
RuntimeArray<T>::~RuntimeArray()
{
  if(contents_)
    delete[] contents_;
}

template<typename T>
T& RuntimeArray<T>::operator[](int index)
{
  return contents_[index];
}

template<typename T>
RuntimeArray<T>::Iterator RuntimeArray<T>::Begin()
{
  return Iterator(contents_);
}

template<typename T>
RuntimeArray<T>::Iterator RuntimeArray<T>::End()
{
  return Iterator(contents_ + size_);
}

How can I make these errors go away? The files make sense to me, but alas, it's the compiler's say that matters.

+1  A: 

That is one heck of a funny style guideline. In general, definitions of template functions have to be in the header file. This came by just a few hours ago: http://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possible

Thomas
I know. It's for college, and I don't agree with half the majority of the style guidelines. However, realize that this isn't part of the assignment, but something I did to solve the assignment. We haven't even gotten to pointers, templates, or even classes yet.
Cristián Romo
+6  A: 

I think that you are missing the typename keyword.

e.g.

template<typename T>
RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator++()

should be

template<typename T>
typename RuntimeArray<T>::Iterator& RuntimeArray<T>::Iterator::operator++()

'Nested' types which are dependent on a template parameter need the typename keyword to tell the compiler that they should be types where this otherwise would be ambiguous.

Charles Bailey
How is the first one ambiguous? I don't see it.
Cristián Romo
At the point at which the compiler reaches `Iterator` it doesn't know what `T` will be, and `Iterator` is dependent - via the `RuntimeArray` template - on `T`. It's possible that `Iterator` might name a member function or a data member for some specializations. In order to correctly parse the declaration the compiler needs to be told that it's a type with the `typename` keyword. Because your type is a nested class it's not as obvious as in some other examples why this rule is needed.
Charles Bailey
+1  A: 

This will not work the way you want it to. All your function declarations and definitions must appear in the .h file in which you defined RuntimeArray. The error you're seeing may be something else, perhaps a typename thing, but even if you can make RunTimeArray.cpp compile in isolation no one will be able to use it.

If you really must have the definitions in a separate file, #include it at the end of runtimearray.h

David Seiler
After Charles Bailey's fix, including the .cpp file allows the client program works. It's not the way *I'd* do it, (heck, as far as I know, the standard library and Boost use the only header approach,) but it's the way the teacher wants it.
Cristián Romo
You might want to point out to your teacher that these kinds of "extern templates" are non-standard and are only supported by a handful of compilers (and some incorrectly, at that).
greyfade
Extern templates? I'm including both the declaration of the class and the definition into the using file. That seems like it'd be the same as if I'd just done it all in one file to me.
Cristián Romo