Question

I'm trying to overload the + operator in a forest class, a forest being a collection of trees, and the + operator is supposed to combine two forests into one. I have the following code as my class definition:

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

The following is my implementation of 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;
}

I get the following error on compile:

|28|error: expected constructor, destructor, or type conversion before '&' token|

line 28 refers to the signature of my operator+ implementation.

I think to correct it i am supposed to add to the return type, giving:

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

But that gives me the following errors:

|28|error: declaration of 'operator+' as non-function| |28|error: missing template arguments before '&' token| |28|error: 'f1' was not declared in this scope| |28|error: missing template arguments before '&' token| |28|error: 'f2' was not declared in this scope|

Can anyone help me with this? I'd be very very thankful.

Was it helpful?

Solution

The key to writing operator+ is don't write operator+. Instead, write a copy ctor and operator+=:

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

Now we add operator+:

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

And that's it! Copying is usually straight-forward (sometimes by being disallowed) and it may be simpler to think in terms of += than + (you have two objects and mutate one, rather than create a third object out of two). This pattern for op+ works with any similar type, and even for similar operators such as -, *, and /.

OTHER TIPS

Operator overloading can be a good or a bad thing. Good when it leads to simpler looking code. Bad when it leads to writers either overloading with incorrect semantics (yet a solution that compiles) or where the intuitive way to use the operator leads to highly inefficient code.

Note the latter statement can apply to std::string too, which could potentially make large numbers of copies, and which is why the C++03 standard states that a string does not have to be stored internally in a contiguous buffer (in the old days they used copy-on-write references to and could store such references to both strings being concatenated until required. Subsequently it was found to be non-threadsafe and making it so was more costly than simply copying the buffer so now they copy every time and are inefficient again).

(Note that the C++11 standard which recognises threading and atomic issues ensures that the underlying does need to be contiguous and null-terminated to make read operations safe).

The correct signature of operator+ (in the case all are the same type) is as follows:

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

As a member function it would be:

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

You can implement operator+ automatically as a template whenever operator += is available with

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

If you want to declare an overloaded operator of your template as a friend, this is the correct way to do it. I will show it with 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;
}

You would apply a similar technique to any other external function you wish to declare as a friend to your class, i.e.

  1. Forwardly declare your class as a template
  2. Forwardly declare the method as a template function
  3. Make the function a friend using <> before the opening parentheses denoting the parameters
  4. Implement the function after your class.

You have to provide a template arguments for all Forest parameters.

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

Also, consider making the arguments const references to make sure you do not manipulate them.

There are several questions on stackoverflow regarding friend function templates. The C++ FAQ also has a page on them that explains some basics.

You can define an operator+ template as follows:

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

Cheers & hth.,

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top