class 层次结构中的运算符重载
Operator overloads within a class heirarchy
如果之前有人问过这个问题并回答过这个问题,我深表歉意,但我一直在寻找一个可以理解的解决方案好几个小时了,但我并不高兴。
我正在尝试实现一个简单的 class 层次结构,其中在基础 class 中定义了运算符重载(因为这些在各种派生的 class 之间没有区别)。但是,由于其中大部分都需要 return 我们所处上下文中任何派生 class 的新对象,我假设它们需要声明为模板化方法,但是,当我尝试这样做编译,链接器给我一个 'Unresolved external symbol' 错误 ...
举个例子:
/// This is in the class header file
class Base
{
// Declare all the base storage and methods ...
public:
template<class T>
friend T operator+(const T& lhs, const T& rhs);
// .... other stuff in here.
}
/// Then in the implementation file
T operator+ (const T& lhs, const T& rhs)
{
return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}
我希望这会导致我能够这样声明派生对象:
class Derived : public Base
{
// ... Add the derived class functionality
// -- Question: Do I need to do anything in here with operator+ ???
}
Derived A();
Derived B();
Derived C = A + B;
是期望的结果,但是没有在每个派生的个体中定义运算符 class,我看不到我可以在 C++ 中实现它的方法,因为模板方法会导致链接器错误。
我是否遗漏了一些非常明显和基本的东西,或者在 C++ 中根本没有简单的方法来做到这一点?
您的评论表明模板 "in the implementation file",这很可能是导致您出现问题的原因。函数模板声明(例如 T operator+(const T&, const&);
声明必须链接的符号——但它需要该模板某处的 实例化 。
简单地在源文件中定义 template
函数实际上并没有实例化代码——它需要明确需要链接的每种类型的具体实例化,或者具有函数模板从打算调用它的地方可见的定义(有关详细信息,请参阅 this answer)。
在许多情况下,最好在头文件中定义函数模板,这样模板的任何调用者都能够实例化函数,而无需链接到其他地方的现有实例化。
也就是说...
您可能需要重新考虑当前的方法。您的 operator+
模板不限制可以考虑的类型 T
,这将导致 operator+(const T&, const T&)
成为 any [=14= 的可行重载] 还没有 operator+
的类型,前提是声明在重载解析期间可见。这可能会导致其他奇怪的编译器/链接器错误。
有几种解决类型约束的方法;可能最简单的方法是使用 SFINAE 通过检查 T
是否派生自 Base
.
来约束它
例如:
/// This is in the class header file
class Base
{
// Declare all the base storage and methods ...
public:
template<class T, class>
friend T operator+(const T& lhs, const T& rhs);
// .... other stuff in here.
}
// Note: In the same header!
// This only enables + if 'T' derives from 'Base'
template <typename T, typename = std::enable_if_t<std::is_base_of<Base,T>::value>>
T operator+(const T& lhs, const T& rhs)
{
return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}
如果之前有人问过这个问题并回答过这个问题,我深表歉意,但我一直在寻找一个可以理解的解决方案好几个小时了,但我并不高兴。
我正在尝试实现一个简单的 class 层次结构,其中在基础 class 中定义了运算符重载(因为这些在各种派生的 class 之间没有区别)。但是,由于其中大部分都需要 return 我们所处上下文中任何派生 class 的新对象,我假设它们需要声明为模板化方法,但是,当我尝试这样做编译,链接器给我一个 'Unresolved external symbol' 错误 ...
举个例子:
/// This is in the class header file
class Base
{
// Declare all the base storage and methods ...
public:
template<class T>
friend T operator+(const T& lhs, const T& rhs);
// .... other stuff in here.
}
/// Then in the implementation file
T operator+ (const T& lhs, const T& rhs)
{
return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}
我希望这会导致我能够这样声明派生对象:
class Derived : public Base
{
// ... Add the derived class functionality
// -- Question: Do I need to do anything in here with operator+ ???
}
Derived A();
Derived B();
Derived C = A + B;
是期望的结果,但是没有在每个派生的个体中定义运算符 class,我看不到我可以在 C++ 中实现它的方法,因为模板方法会导致链接器错误。
我是否遗漏了一些非常明显和基本的东西,或者在 C++ 中根本没有简单的方法来做到这一点?
您的评论表明模板 "in the implementation file",这很可能是导致您出现问题的原因。函数模板声明(例如 T operator+(const T&, const&);
声明必须链接的符号——但它需要该模板某处的 实例化 。
简单地在源文件中定义 template
函数实际上并没有实例化代码——它需要明确需要链接的每种类型的具体实例化,或者具有函数模板从打算调用它的地方可见的定义(有关详细信息,请参阅 this answer)。
在许多情况下,最好在头文件中定义函数模板,这样模板的任何调用者都能够实例化函数,而无需链接到其他地方的现有实例化。
也就是说...
您可能需要重新考虑当前的方法。您的 operator+
模板不限制可以考虑的类型 T
,这将导致 operator+(const T&, const T&)
成为 any [=14= 的可行重载] 还没有 operator+
的类型,前提是声明在重载解析期间可见。这可能会导致其他奇怪的编译器/链接器错误。
有几种解决类型约束的方法;可能最简单的方法是使用 SFINAE 通过检查 T
是否派生自 Base
.
例如:
/// This is in the class header file
class Base
{
// Declare all the base storage and methods ...
public:
template<class T, class>
friend T operator+(const T& lhs, const T& rhs);
// .... other stuff in here.
}
// Note: In the same header!
// This only enables + if 'T' derives from 'Base'
template <typename T, typename = std::enable_if_t<std::is_base_of<Base,T>::value>>
T operator+(const T& lhs, const T& rhs)
{
return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}