我们如何将带有 class 模板的友元函数声明到 .h 文件中并将它们定义到 .cpp 文件中(不是全部在一个头文件中)?
How do we declare a friend function with a class template into .h file and define them into a .cpp file (not all in one header file)?
分离(友元函数+class模板)的declaration/definition时出现错误:
error LNK2001: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Property<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$Property@H@@@Z)
请注意,在没有 class 模板的情况下使用友元函数时一切正常,反之亦然
但是当同时使用这两个东西时会发生错误。
我尝试在 .cpp 文件中实例化模板,但没有成功。
另外,我在 .h 文件的末尾包含了 .cpp 文件,但它也不起作用。
class.h
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other);
};
class.cpp
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template <class PropertyType>
std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other) {
os << other.m_property;
return os;
}
template Property<int>;
main.cpp
int main() {
Property<int> num;
num = 100;
std::cout << num << "\n";
}
把declaration/definition分成两个文件一起使用有什么问题?
您的代码片段有两个问题。
第一个问题是您将实现放在源文件而不是头文件中。所以要解决这个问题,只需将实现移到头文件中即可。
第二个问题是即使将实现移到源文件中,程序仍然无法运行(Demo)。这是因为您当前拥有的朋友声明(用于重载opearator<<
)是用于普通(non-template)函数.也就是说,在您的原始代码中,operator<<
for class template Property<>
不是函数模板,而是在需要时使用 class 模板实例化的“普通”函数。这就是我们所说的 模板实体 .
但是您在源文件 (.cpp) 中为重载运算符 << 提供的 定义 是针对函数模板而不是针对普通函数的。因此,对于语句 std::cout << num << "\n";
,链接器无法找到对应于您具有友元声明的普通重载 operator<<
的 definition/implementation。
有两种方法可以解决这个问题:
方法一
在朋友声明中添加一个单独的参数子句。
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter cluase added here
//---------------------------------------------------vvvvv----------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
方法二
这里我们为重载的 operator<<
.
提供前向声明
//forward declaration for class template Property
template<typename T> class Property;
//forward declaration for overloaded operator<<
template<typename T> std::ostream& operator<<(std::ostream&,const Property<T>&);//note the const in the second parameter
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
//---------------------------------vvvvvvvvvvvvvv---------------------------------> angle brackets used here
friend std::ostream& operator<<<PropertyType>(std::ostream& os,const Property<PropertyType>& other);//also note the const in the second parameter
};
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
方法三
如果你想在源文件而不是头文件中提供实现,那么你应该添加
template std::ostream& operator<<(std::ostream& os, Property<int>& other);
在源文件中除了为友元声明添加模板参数子句外,如下所示:
class.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <iostream>
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter clause added
//---------------------------------------------------vvvvv--------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
#endif
class.cpp
#include "class.h"
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template<typename PropertyType>
//----------------------------------------vvvvv------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
template class Property<int>;
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);
main.cpp
#include <iostream>
#include "class.h"
int main() {
Property<int> num;
num = 100;
std::cout << num << "\n";
}
我所做的更改包括:
为好友声明添加了一个单独的模板参数子句。这是为了使友元声明用于函数模板。此外,我们指定了一个名为 T
而不是 PropertyType because otherwise the new
PropertyTypewill shadow the outer
PropertyType` 的不同类型参数。
在重载的opeartor<<
.
的第二个参数中添加了一个low-level const
在方法3中,在源文件(class.cpp)中,添加
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);
对于 non-member 函数重载 operator<<
。
分离(友元函数+class模板)的declaration/definition时出现错误:
error LNK2001: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Property<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$Property@H@@@Z)
请注意,在没有 class 模板的情况下使用友元函数时一切正常,反之亦然 但是当同时使用这两个东西时会发生错误。
我尝试在 .cpp 文件中实例化模板,但没有成功。
另外,我在 .h 文件的末尾包含了 .cpp 文件,但它也不起作用。
class.h
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other);
};
class.cpp
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template <class PropertyType>
std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other) {
os << other.m_property;
return os;
}
template Property<int>;
main.cpp
int main() {
Property<int> num;
num = 100;
std::cout << num << "\n";
}
把declaration/definition分成两个文件一起使用有什么问题?
您的代码片段有两个问题。
第一个问题是您将实现放在源文件而不是头文件中。所以要解决这个问题,只需将实现移到头文件中即可。
第二个问题是即使将实现移到源文件中,程序仍然无法运行(Demo)。这是因为您当前拥有的朋友声明(用于重载opearator<<
)是用于普通(non-template)函数.也就是说,在您的原始代码中,operator<<
for class template Property<>
不是函数模板,而是在需要时使用 class 模板实例化的“普通”函数。这就是我们所说的 模板实体 .
但是您在源文件 (.cpp) 中为重载运算符 << 提供的 定义 是针对函数模板而不是针对普通函数的。因此,对于语句 std::cout << num << "\n";
,链接器无法找到对应于您具有友元声明的普通重载 operator<<
的 definition/implementation。
有两种方法可以解决这个问题:
方法一
在朋友声明中添加一个单独的参数子句。
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter cluase added here
//---------------------------------------------------vvvvv----------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
方法二
这里我们为重载的 operator<<
.
//forward declaration for class template Property
template<typename T> class Property;
//forward declaration for overloaded operator<<
template<typename T> std::ostream& operator<<(std::ostream&,const Property<T>&);//note the const in the second parameter
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
//---------------------------------vvvvvvvvvvvvvv---------------------------------> angle brackets used here
friend std::ostream& operator<<<PropertyType>(std::ostream& os,const Property<PropertyType>& other);//also note the const in the second parameter
};
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
方法三
如果你想在源文件而不是头文件中提供实现,那么你应该添加
template std::ostream& operator<<(std::ostream& os, Property<int>& other);
在源文件中除了为友元声明添加模板参数子句外,如下所示:
class.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <iostream>
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter clause added
//---------------------------------------------------vvvvv--------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
#endif
class.cpp
#include "class.h"
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}
template<typename PropertyType>
//----------------------------------------vvvvv------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
template class Property<int>;
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);
main.cpp
#include <iostream>
#include "class.h"
int main() {
Property<int> num;
num = 100;
std::cout << num << "\n";
}
我所做的更改包括:
为好友声明添加了一个单独的模板参数子句。这是为了使友元声明用于函数模板。此外,我们指定了一个名为
T
而不是PropertyType because otherwise the new
PropertyTypewill shadow the outer
PropertyType` 的不同类型参数。在重载的
的第二个参数中添加了一个low-levelopeartor<<
.const
在方法3中,在源文件(class.cpp)中,添加
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);
对于 non-member 函数重载 operator<<
。