views:

138

answers:

5
template<typename AT>
class growVector {

     int size;
     AT **arr;
     AT* defaultVal;

    public:

     growVector(int size , AT* defaultVal);   //Expects number of elements (5) and default value (NULL)
     AT*& operator[](unsigned pos);
     int length();
     void reset(int pos); //Resets an element to default value
     void reset();   //Resets all elements to default value
     ~growVector();
};

and .cpp is

template<typename AT>
growVector<AT>::growVector(int size, AT* defaultVal) {
    arr = new AT*[size];
    this->size = size;
    for (int i = 0 ; i < size; i++){
     arr[i] = defaultVal;
    }
}

template<typename AT>
AT*& growVector<AT>::operator [](unsigned pos){
    if (pos >= size){
     int newSize = size*2;
     AT** newArr = new AT*[newSize];
     memcpy(newArr, arr, sizeof(AT)*size);
     for (int i = size; i<newSize; i++)
      newArr[i] = defaultVal;
     size = newSize;
     delete arr;
     arr = newArr;
    }
    return arr[pos];
}

//template<typename AT>
//const AT*& growVector<AT>::operator [](unsigned pos) const{
//  if (pos >= size)
//   throw std::range_error("index out of range in constant vector");
//  }
//  return NULL;
//}
template<typename AT>
int growVector<AT>::length(){
    return size;
}

template<typename AT>
growVector<AT>::~growVector(){
    delete arr;
}

template<typename AT>
void growVector<AT>::reset(int pos){
    if (pos>=size)
     throw new std::range_error("index out of bounds");
    arr[pos] = defaultVal;
}

template<typename AT>
void growVector<AT>::reset(){
    for (int pos = 0; pos<size; pos++)
     arr[pos] = defaultVal;
}

...very basic.

I use it in

int main() {

    growVector<char> gv(5, (char*)NULL);
    char* x = NULL;
    for (int i = 0; i< 50; i++){
     gv[i] = x;
    }
    gv.reset();
    return 0;
}

it compiles but the linker says:

Invoking: GCC C++ Linker
g++ -pthread -o"base"  ./src/base.o  ./src/base/baseController.o ./src/base/baseThreads.o ./src/base/utils.o  ./src/base/utils/utilClasses.o   
./src/base.o: In function `main':
/home/dario/workspace/base/Debug/../src/base.cpp:95: undefined reference to `baseUtils::growVector<char>::growVector(int, char*)'
/home/dario/workspace/base/Debug/../src/base.cpp:98: undefined reference to `baseUtils::growVector<char>::operator[](unsigned int)'
/home/dario/workspace/base/Debug/../src/base.cpp:100: undefined reference to `baseUtils::growVector<char>::reset()'
/home/dario/workspace/base/Debug/../src/base.cpp:101: undefined reference to `baseUtils::growVector<char>::~growVector()'
/home/dario/workspace/base/Debug/../src/base.cpp:101: undefined reference to `baseUtils::growVector<char>::~growVector()'
collect2: ld returned 1 exit status

I'm really clueless

+3  A: 

Try placing the definition in the header file too. For details, you can read this FAQ.

ltcmelo
+7  A: 

For templates, in general, the definition should be placed in the header file as it is needed by all compilation units.

There are tricks to avoid doing that when you know with which types your template class is going to be invoked but for truly generic problems, it just does not work.

Note:

If I were you I would use a STL container, the result is less likely to be buggy.

Matthieu M.
+1  A: 

The short story: you need to put the whole implementation of the template in the header.

In theory, C++ has a keyword ("export") that allows you to separate templates about like you would other code (declarations in a header, bodies in a .cpp file). Unfortunately, there's only one compiler (Comeau C++) that implements the export keyword, so this option isn't available for most people. I believe if you use an undocumented switch or two, Intel C++ also implements the export keyword to some degree, but it's not supported, so it may be open to question whether it really works dependably or not (I'm sure the parsing part does, but I wouldn't be surprised if other parts had some problems).

Jerry Coffin
+1  A: 

The compiler needs to see both the declaration and definition in order to instantiate a template. As you did provide an explicit instantiation/specialisation of growVector in the .cpp, there is no way the compiler can generate the instantiated template for you. That's one of the main reasons that you'll find that template classes tend to be declared and defined within the scope of a header file.

You can usually rely on a modern linker to be smart enough to remove duplicate instantiations of the same template, but they aren't necessarily capable of instantiating templates.

Timo Geusch
+1  A: 

Did you leave out the reference to the baseUtils namespace?

/home/dario/workspace/base/Debug/../src/base.cpp:95: undefined reference to `baseUtils::growVector<char>::growVector(int, char*)'

I didn't see it in your main() code and your class definition doesn't inherit from any other class.

Kelly French