mskoryk
mskoryk

Reputation: 506

operator overloading as a friend function

I create a template class and want to overload an operator + (several times). I do this in the following way

template <typename T> class Polynomial;

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);

....

template <typename T>
class Polynomial {
    ....

    public:

    ....

    Polynomial operator +(const Polynomial& other) const {
       // impelementation
    }

    friend const Polynomial<T> operator + <> (const Polynomial<T>& poly, const T& scalar);

};

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
    // implementation
}

However, I got the following error (which corresponds to the line that begins with 'friend')

problem2.cpp:282:45: error: declaration of ‘operator+’ as non-function
problem2.cpp:282:45: error: expected ‘;’ at end of member declaration
problem2.cpp:282:47: error: expected unqualified-id before ‘<’ token

Following Raxvan's advise, I've changed the code

template class Polynomial;

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);

template <typename T>
ostream& operator <<(ostream& out, const Polynomial<T>& other);

....

template <typename T>
class Polynomial {
    ....

    public:

    ....

    friend ostream& operator << <> (ostream& out, const Polynomial<T>& other);

    Polynomial operator +(const Polynomial& other) const {
       // impelementation
    }

    template <typename NOT_T>
    friend const Polynomial<NOT_T> operator +(const Polynomial<NOT_T>& poly, const NOT_T& scalar);

};

template <typename T>
ostream& operator <<(ostream& out, const Polynomial<T>& other) {
    // implementation
}

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
    // implementation
}

And in this code I don't have problems with operator << as I had with operator +. Can anyone explain the defference?

Upvotes: 0

Views: 696

Answers (3)

James Kanze
James Kanze

Reputation: 153889

The problem is subtle. Your syntax is very close to being correct. I think your friend declaration should be:

friend Polynominal<T> const operator+<T>( ... );

but both VC++ and g++ accept:

friend Polynominal<> const operator+<T>( ... );

when they can see the declaration:

template <typename T>
const Polynomial<T> operator+(const Polynomial<T>& poly, const T& scalar);

(I can't find anything in the standard to justify this off hand, but since both VC++ and g++ do it, I suppose that it's something I've missed.)

And therein lies the problems. The declaration at namespace scope of operator+ is hidden by the in-class definition. The compiler complains because the operator+ it finds (the in class definition) isn't a template itself (although it is a member of a class template).

If you don't have the problem with operator<<, it's because you don't have a member function with the same name.

There are several ways of solving this problem. The simplest is probably to make all of the operator+ friends. Or not: the most common approach to this is to implement operator+ in terms of operator+= (which should be a member, in all cases). In which case, operator+ doesn't have to be a friend. Or you don't make operator+ a template at all, but definite it inside your class template:

template <typename T>
class Polynomial
{
    friend Polynomial operator+( Polynomial const& lhs, Polynomial const& rhs )
    {
        //  ...
    }
    friend Polynomial operator+( Polynomial const& lhs, T const& rhs )
    {
        //  ...
    }
    friend Polynomial operator+( T const& lhs, Polynomial const& rhs )
    {
        //  ...
    }
}

(Note that the functions being defined are not templates, but separate overloaded non-template functions, one for each instantiation of Polynomial. But the results end up being the same.)

In this case, you probably would want to have a member function, called by the operator+ functions, which would do the actual work; you don't want too much code directly inline like this.

Upvotes: 2

Raxvan
Raxvan

Reputation: 6505

You have to have the same definition when you label a function as friend, this includes the template used above with another type , not T

template <typename T> class Polynomial;

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);

template <typename T>
class Polynomial {
public:

    template < class NOT_T> //must respect the deffinition from above
    friend const Polynomial<NOT_T> operator + (const Polynomial<NOT_T>& poly, const NOT_T& scalar);

};

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar)
{
    //...
}

Edit:

For a simplified explanation i have changed the function to foo and bar to illustrate the difference in declarations:

template <typename T> class Polynomial;

template <typename T>
void bar(const Polynomial<T>& );

void foo(const Polynomial<float> & );//foo for speciffic float

template <typename T>
class Polynomial {
public:
    template <typename> //this is also valid declaration; 
    friend void bar(const Polynomial<T> & );
    //it just has to have template because it's a template functions declaration

    //not a valid declaration:
    //friend void bar <> (const Polynomial<T> & );

    //this declaration has no template;
    //it refers to a foo function specific to Polynomial<T> type
    //so if you use Polynomial<float> there must be a foo for floats
    friend void foo(const Polynomial<T> &);
};

template <class T>
void bar(const Polynomial<T>&)
{
}

void foo(const Polynomial<float> &)
{
}

void main()
{
    Polynomial<float>   pf;
    Polynomial<int>     pi;

    foo(pi);//error since there is not **foo** declared for int 
    foo(pf);//ok; we have **foo** for  Polynomial<float>

    bar(pf);
    bar(pi);//both ok since bar is templated function;
}

Raxvan.

Upvotes: 1

Nim
Nim

Reputation: 33655

Your operator+ is a function template, to make this a friend, you need to declare it fully including the template parameters, however with a different template parameter, for example:

#include <iostream>

template <typename T> class Polynomial;

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar);

template <typename T>
class Polynomial {
    public:

    Polynomial operator +(const Polynomial& other) const {
      std::cout << "inline +" << std::endl;
    }

    // Here introduce a different type to indicate that this is a template...
    template <typename U>
    friend const Polynomial<U> operator + (const Polynomial<U>& poly, const U& scalar);

};

template <typename T>
const Polynomial<T> operator +(const Polynomial<T>& poly, const T& scalar) {
    std::cout << "friend +" << std::endl;
}

int main(void)
{
  Polynomial<int> f;

  f = f + 1;

  f = f + 1.; // this fails

  f = f + Polynomial<int>();
}

Upvotes: 1

Related Questions