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