4

我试图在森林类中重载 + 运算符,森林是树木的集合,而 + 运算符应该将两个森林合并为一个。我有以下代码作为我的类定义:

template<typename NODETYPE>
class Forest
{


    public:

        friend Forest& operator+<>(Forest&, Forest&);
        friend ostream& operator<<<>(ostream&, const Forest&);
        friend istream& operator>><>(istream&, Forest&);
        Forest();
        Forest( const Forest& otherForest);
        ~Forest();
        void nodes(int&) const;

    private:
        ForestNode<NODETYPE> *root;

        ForestNode<NODETYPE> *getNewNode( const NODETYPE &);
};

以下是我对operator+的实现:

template<typename NODETYPE>
Forest& operator+<>(Forest& f1, Forest& f2)
{
    f3 = new Forest();
    f3.root = *f1.*root;
    f3.root.sibling = *f2.*root;
    *f1.root = 0;
    *f2.root = 0;
    return f3;
}

我在编译时收到以下错误:

|28|错误: '&' 标记之前的预期构造函数、析构函数或类型转换|

第 28 行是我的 operator+ 实现的签名。

我认为要纠正它,我应该添加到返回类型中,给出:

template<typename NODETYPE>
Forest<NODETYPE>& operator+<>(Forest& f1, Forest& f2)
{
    f3 = new Forest();
    f3.root = *f1.*root;
    f3.root.sibling = *f2.*root;
    *f1.root = 0;
    *f2.root = 0;
    return f3;
}

但这给了我以下错误:

|28|错误: 'operator+' 声明为非函数| |28|错误:在“&”标记之前缺少模板参数| |28|错误: 'f1' 没有在这个范围内声明| |28|错误:在“&”标记之前缺少模板参数| |28|错误: 'f2' 没有在这个范围内声明|

谁能帮我这个?我会非常非常感谢。

4

4 回答 4

6

写operator+的关键是不要写operator+。 相反,编写一个复制 ctor 和 operator+=:

template<class NodeType>
struct Forest {
  //...
  Forest(Forest const &other);
  //...
  Forest& operator+=(Forest const &other) {
    // code here
    return *this;
  }
  //...
};

现在我们添加 operator+:

template<class NodeType>
struct Forest {
  //...
  friend Forest operator+(Forest a, Forest const &b) {
    a += b;
    return a;
  }
  //...
};

就是这样!复制通常是直截了当的(有时被禁止),并且从 += 的角度考虑可能比 + 更简单(你有两个对象并改变一个,而不是从两个对象中创建第三个对象)。op+ 的这种模式适用于任何类似的类型,甚至适用于类似的运算符,例如 -、* 和 /。

于 2010-10-27T10:59:06.733 回答
3

运算符重载可能是好事也可能是坏事。当它导致看起来更简单的代码时很好。当它导致编写者使用不正确的语义重载(但可以编译的解决方案)或使用运算符的直观方式导致代码效率非常低时,这是不好的。

请注意,后一个语句也可以应用于 std::string,这可能会产生大量副本,这就是 C++03 标准规定字符串不必在内部存储在连续缓冲区中的原因(在过去他们使用写时复制引用,并且可以将此类引用存储到被连接的两个字符串,直到需要为止。随后发现它是非线程安全的,因此比简单地复制缓冲区成本更高,所以现在他们复制每个时间和效率再次低下)。

(请注意,识别线程和原子问题的 C++11 标准确保底层确实需要是连续的和空终止的,以使读取操作安全)。

operator+的正确签名(如果都是同一类型)如下:

T operator+( const T&, const T& );

作为成员函数,它将是:

class T
{
    // make public if necessary
    T operator+( const T& rhs ) const;
};

只要 operator += 可用,您就可以将 operator+ 自动实现为模板

template<typename T, typename R>
T operator+( const T& lhs, const R& rhs )
{
    T copy(lhs);
    return copy += rhs;
}

如果您想将模板的重载运算符声明为友元,这是正确的方法。我会用 operator<<

// first some forward declarations, assume ostream already declared with #include <iosfwd> minimum
template< typename T > class Forest;
template< typename T > std::ostream & operator<<( std::ostream & os, const Forest<T> & for );

template< typename T> class Forest
{
     friend std::ostream& operator<< <>( std::ostream&, const Forest<T> & );
     //rest of class Forest
};

template< typename T >
std::ostream & operator<<( std::ostream& os, const Forest<T> & forest )
{
    // implement
    return os;
}

您可以将类似的技术应用于您希望声明为类的朋友的任何其他外部函数,即

  1. 将您的类前向声明​​为模板
  2. 将方法前向声明为模板函数
  3. 在表示参数的左括号之前使用 <> 使函数成为朋友
  4. 在您的课后实现该功能。
于 2010-10-27T11:09:26.663 回答
2

您必须为所有参数提供模板Forest参数。

template<typename NODETYPE>
Forest<NODETYPE> operator+(Forest<NODETYPE>& f1, Forest<NODETYPE>& f2)

此外,请考虑将参数设置为const引用,以确保您不会操纵它们。

stackoverflow 上有几个 关于友元函数模板的问题。C++ FAQ也有一个关于它们的页面,解释了一些基础知识。

于 2010-10-27T10:38:01.060 回答
-1

operator+您可以按如下方式定义模板:

template< class NodeType >
Forest<NodeType> operator+( Forest<NodeType> const& f1, Forest<NodeType> const& f2)
{
    // Implementation.
}

干杯&hth.,

于 2010-10-27T10:46:59.583 回答