模板类,友元运算符<<重载

3

我试图为一个模板类重载"<<"运算符。该类的定义在.h文件中,实现在.cpp文件中。

/tmp/ccjJIJhO.o: In function `main':
main.cpp:(.text+0xad): undefined reference to `std::basic_istream<char, std::char_traits<char> >& operator>><int>(std::basic_istream<char, std::char_traits<char> >&, FeatureVector<int>&)'
main.cpp:(.text+0xba): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& operator<< <int>(std::basic_ostream<char, std::char_traits<char> >&, FeatureVector<int> const&)'
collect2: ld returned 1 exit status

类定义:

common.h

#include <iostream>
using namespace std;

featurevector.h

#ifndef FEATURE_VECTOR_H
#define FEATURE_VECTOR_H

#include <common.h>

template < class FEAT_TYPE >
class FeatureVector;

template < class FEAT_TYPE >
istream & operator >> (istream &, FeatureVector<FEAT_TYPE> &);

template < class FEAT_TYPE >
ostream & operator << (ostream &, const FeatureVector<FEAT_TYPE> &);

template < class FEAT_TYPE >
class FeatureVector{
    public:
        FeatureVector(int = 0);
        ...
        friend istream & operator >> <>(istream &, FeatureVector<FEAT_TYPE> & );
        friend ostream & operator << <>(ostream &, const FeatureVector<FEAT_TYPE> &);
        ...
        ~FeatureVector();

    private:
        int m_nDim;
        FEAT_TYPE * m_pFeat;
};
#endif

featurevector.cpp

#include <featurevector.h>
...
template < class FEAT_TYPE >
istream & operator >> (istream & input, FeatureVector< FEAT_TYPE> & refFeat ){

    int d;

    for(d=0; d < refFeat.getDim(); d++){
        input >> refFeat.m_pFeat[d];
    }

    return (input);
}

template < class FEAT_TYPE >
ostream & operator << (ostream & output, const FeatureVector< FEAT_TYPE > & refFeat ){

    int d;

    for(d=0; d < refFeat.getDim(); d++){
        output << refFeat.m_pFeat[d] << " ";
    }

    output << endl;

    return (output);
}
...
#include "featurevector-impl.cpp"

featurevector-impl.cpp

template class FeatureVector<int>;
//template istream & operator >> <>(istream &, FeatureVector<int> &);
//template ostream & operator << <>(ostream &, const FeatureVector<int> &);

mylib.h

#ifndef MY_LIB_H
#define MY_LIB_H
#include <featurevector.h>
#endif

main.cpp

#include <mylib.h>
#include <common.h>

int main(){
    FeatureVector<int> pFeat(10);
    cin >> (pFeat);
    cout << (pFeat);

    return (0);
}

与“mylib”相关的Makefile

INC=./inc
SRC=./src
LIB=./lib
OBJ=./obj

CC=g++
CFLAGS=-O3 -Wall

mylib: $(LIB)/mylib.a
echo "mylib was created!..."

$(LIB)/mylib.a: \
$(OBJ)/featurevector.o 
    ar csr $(LIB)/mylib.a \
$(OBJ)/featurevector.o 

$(OBJ)/featurevector.o: $(SRC)/featurevector.cpp
    $(CC) -c $(CFLAGS) $(SRC)/featurevector.cpp -I$(INC)  \
    -o $(OBJ)/featurevector.o

clean:
    rm -rf $(LIB)/*.a
    rm -rf $(OBJ)/*.o

为main.cpp编写Makefile(main.cpp和其Makefile位于“app”目录下)

LIB=../lib
INC=../inc
OBJ=../obj
BIN=../bin

CC=g++
CFLAGS=-O3 -Wall
LFLAGS=-lmylib -lm

$@.cpp: $(LIB)/mylib.a $@.cpp
    cd ..; make; cd app;
$(CC) $(CFLAGS) $@.cpp -o $(BIN)/$@ -I$(INC) -L$(LIB) $(LFLAGS)

clean:
    rm -rf $(BIN)/*

这是一个打印输出重载运算符的代码,其中出现了一个括号似乎是打错了。 - bjskishore123
2
是同样的代码导致了错误吗?错误出现在运算符>>而不是<<上。 - Rod
1
永远不要从头文件中包含.cpp文件。只需给它取一个不同的名称:blah_detail.hpp、_blah.inc等。 - Roger Pate
@Roger:我听说过.ipp.tpp;如果我采用这种风格,我可能会选择后者。 - GManNickG
@Steve:啊,你说得对。在.cpp文件中仍然应该非常少这样做。 - Roger Pate
显示剩余5条评论
5个回答

4
根据这里的说法,在类定义中,您必须将函数声明为模板函数。 class.h
#include <iostream>
using std::ostream;

template <typename T>
class A {
  public:
    ...

    template <typename J> // <-- CAUTION!
    friend ostream &operator<<(ostream &output, const A<J> &a);
};

class.cpp

#include "class.h"
...
template <typename T>
ostream &operator<<(ostream &output, const A<T> &a) {
  // Your implementation
  return output;
}

...
template ostream &operator<<(ostream &output, const A<int> &a);
template ostream &operator<<(ostream &output, const A<float> &a);

如果删除行template <typename J>,则会出现编译错误“未定义的引用”。

2

我认为这不是问题所在。Javier直接从H文件中#include HPP文件,与您的FAQ链接产生相同的结果。 - John Dibling
@John,@Jahvier:我看不到main函数中operator>>函数模板的定义。我也看不到它的显式实例化。如果有的话,请原谅我的打扰。但是,如果有的话,我想这个问题就不会被提出来了。祝好! - Cheers and hth. - Alf
@Javier:好的,那你能告诉我们这是否解决了问题,如果没有,能否发布一些实际的代码? - Cheers and hth. - Alf
它没有解决问题,我还在原帖中附上了实际的代码。 - Javier
@Javier:在我发表评论的时候,以及我写这篇文章的时候,你正在为类型int实例化你的类。但是你没有实例化运算符模板(它们不是类的一部分)。由于定义不可用于main,并且没有实例化,因此会出现链接器错误。请参见上面链接的FAQ第35.12项。祝好! - Cheers and hth. - Alf
显示剩余5条评论

1

您发布的错误代码显示出现了未解决的外部错误,是由于 operator>> 而不是 operator<<。此外,您的代码无法编译,因为 myClass 上没有接受 int 的转换构造函数。因此,您没有发布正确的代码。

但是以下代码可以正常工作:

#include <iostream>
using namespace std;

template < class T >
class myClass;

template < class T >
ostream & operator << (ostream &, const myClass<T> &);

template < class T >
class myClass{
    public:
        myClass(int) {}
        friend ostream & operator << <>(ostream &, const myClass<T> &);

    private:
        T m_Data;
};

template < class T >
ostream & operator << (ostream & out, const myClass<T> & refClass){
    out << refClass.m_Data << endl;
    return (out);
}

myClass<int>;
myClass<float>;



int main(int argc, char **argv){
    myClass<int> test(5);
    cout << test;
    return 0;
}

您好,正如我所提到的那样,我没有放整个类定义。当然,我有一个默认构造函数。所以,您的建议是将所有内容都放在同一个文件中?这就是问题所在吗?出于组织目的,我更喜欢保持文件分离。 - Javier
@Javier:不,最终我遇到的问题是你发布的错误消息抱怨operator>>而不是operator<< - John Dibling
@Javier:此外,如果您能够发布一个最小且完整的示例来复制您遇到的问题,那将非常有帮助。像您发布的伪代码一样的内容很难处理。 - John Dibling
嗨John,我已经更新了类,因为我试图重载“>>”和“<<”,但是出现了类似的错误。 - Javier

0

featurevector-impl.cpp是不正确的。显式模板实例化应该像这样:

template class FeatureVector<int>;

由于运算符不是成员,因此它们也必须显式实例化:

template istream & operator >> <>(istream &, FeatureVector<int> &);

我不建议像这样拆分模板定义,除非你真的很想微观管理哪些特定类将与你的模板一起使用(这有点违背使用模板的精神)。

嗨Steve,最近我尝试了你的建议,但是我仍然有相同的错误。我更新了我的初始帖子,在那里我包括了真实的类。 - Javier
你的编译指令是什么?你是否同时编译main.cpp和featurevector.cpp?(如果你分别编译了它们,则是main.o和featurevector.o) - Steve M
@Steve,我正在分别编译它们。 - Javier
@Javier:那么当你链接它们时,你是在命令中指定两个目标文件吗?如果我在featurevector-impl.cpp中为流操作符进行显式实例化,则即使进行分离编译,这对我来说也可以编译、链接和运行。 - Steve M
嗨,史蒂夫,我基本上有一个包含特征向量类的小型库(因此,在这一点上,我生成了featurevector.o)。然后,当我编译主程序时,我链接库,如:g++ -O3 -Wall main.cpp -o ../bin/main -I../inc -L../lib -lmylib -lm。 - Javier
@Steve,看起来问题出在链接过程中。你能否检查一下我在原帖中添加的Makefile?你在那里看到了什么奇怪的东西吗? - Javier

0

不要把它弄得太复杂:

template<class FEAT_TYPE>
struct FeatureVector {
  FeatureVector(int = 0);

  friend std::istream& operator>>(std::istream &s, FeatureVector &x) {
    for(int d = 0; d < x.getDim(); d++) {
      s >> x.m_pFeat[d];
    }
    return s;
  }

  friend std::ostream& operator<<(std::ostream &s, FeatureVector const &x) {
    // since you're terminating with " " rather than separating:
    copy(x.m_pFeat, x.m_pFeat + x.getDim(), ostream_iterator<FEAT_TYPE>(s, " "));
    s << endl;
    return s;
  }

  //...

谢谢您的建议!但出于某些原因,我需要使用类来工作。 - Javier
@Javier:'struct' 声明一个类。 - Roger Pate
@Roger:谢谢,我不知道那个。我的方法哪里有问题?我不理解出了什么错。 - Javier
@Javier:我不确定问题具体是什么,因为问题已经更新了更多信息,我不愿意假设知道你在使用什么,但就目前的问题文本而言,这些函数的定义被注释掉了。- 我在这里的回答是说你把这个问题复杂化了,然后展示了如何避免这种情况。 - Roger Pate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接