c ++模板类没有链接

时间:2009-11-13 14:33:13

标签: c++ templates linker

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();
};

和.cpp是

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;
}

......非常基本。

我在

中使用它
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;
}

它编译,但链接器说:

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

我真的很无能

5 个答案:

答案 0 :(得分:7)

对于模板,通常,定义应放在头文件中,因为所有编译单元都需要它。

当您知道要调用模板类的类型时,有一些技巧可以避免这样做,但对于真正的通用问题,它只是不起作用。

注意

如果我是你,我会使用一个STL容器,结果不太可能是错误的。

答案 1 :(得分:3)

尝试将定义放在头文件中。有关详细信息,请参阅this FAQ

答案 2 :(得分:1)

简短的故事:您需要将模板的整个实现放在标题中。

理论上,C ++有一个关键字(“export”),它允许您像其他代码一样分隔模板(标题中的声明,.cpp文件中的主体)。不幸的是,只有一个编译器(Comeau C ++)实现了export关键字,因此大多数人都无法使用此选项。我相信如果你使用一个或两个未记录的开关,英特尔C ++也在某种程度上实现了export关键字,但是它不受支持,因此可能会有疑问它是否真的可靠地工作(我确定解析部分确实如此) ,但如果其他部分出现问题,我也不会感到惊讶。)

答案 3 :(得分:1)

编译器需要同时查看声明和定义才能实例化模板。正如您在.cpp中提供了growVector的显式实例化/特化,编译器无法为您生成实例化模板。这是您发现模板类倾向于在头文件范围内声明和定义的主要原因之一。

您通常可以依靠现代链接器足够智能删除同一模板的重复实例,但它们不一定能够实例化模板。

答案 4 :(得分:1)

您是否遗漏了对baseUtils名称空间的引用?

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

我没有在你的main()代码中看到它,你的类定义也没有从任何其他类继承。