Search code examples
c++templatespolymorphismoperator-overloadingfriend

Syntax for C++ operator overloading in a class


Background

I am self-learning C++ from this course. I am trying to overload an operator in a class using a friend function (Assignment 4.2).

EDIT

The linked question does not answer my question. The accepted solution to that question gives a way for templates to be implemented in header + cpp file (not all in the same file).

In fact, I have consulted that question to partially come up with my situation.

My attempt

Using Approach 2, I have almost gotten my code to work (see the cryptic error message). It turns out I am missing a <>. (Solution manual).

I have tried googling, but no-one else had the situations of

  1. friend function,
  2. operator overloading, and
  3. in a templated class

simultaneously.

My rationale

You should not use a class public function to do operator overloading, because the object who calls the function will be implicitly passed, taking up one function parameter. It is better code-style to have the overloaded operator be symmetric (both in usage and in definition).

The usage of friend was suggested by the lecture notes.

Question

  1. Why is <> needed?
  2. Is there a better approach to doing operator overloading in a templated class?
  3. (Optional) How can I make sense of the error message?

Thank you.

Code

stack.h

#include <iostream>
#include <vector>
using std::cout;
using std::vector;

template <typename T>
class Stack;

template <typename T>
Stack<T> operator+(Stack<T> a, Stack<T> b);

template <typename T>
class Stack { // use default constructors and destructors
private:
    vector<T> s;
public:
    bool empty();
    void push(const T &item);
    T& top();
    void pop();
    friend Stack<T> operator+(Stack<T> a, Stack<T> b); // need operator+<>
};

stack.cpp

#include <iostream>
#include <vector>
using std::cout;
#include "stack.h"

template <typename T>
bool Stack<T>::empty() {
    return s.empty();
}

template <typename T>
void Stack<T>::push(const T &item) {
    s.push_back(item);
}

template <typename T>
T& Stack<T>::top() {
    return s.back();
}

template <typename T>
void Stack<T>::pop() {
    s.pop_back();
}

template <typename T>
Stack<T> operator+(Stack<T> a, Stack<T> b) {
    Stack<T> temp;
    while (!b.empty()) {
        temp.push(b.top()); 
        b.pop();
    }
    while (!a.empty()) {
        temp.push(a.top());
        a.pop();
    }
    Stack<T> c;
    while (!temp.empty()) {
        c.push(temp.top());
        temp.pop();
    }
    return c;
}

int main() {
    Stack<int> a, b;
    a.push(1);
    a.push(2);
    b.push(3);
    b.push(4);
    Stack<int> c = a + b;
    cout << c.top() << "\n";

    return 0;
}

Error message

Undefined symbols for architecture x86_64:
  "operator+(Stack<int>, Stack<int>)", referenced from:
      _main in stack-d2f02a.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1

Solution

  • You need to add <> (or <T>) to make the friend declaration referring to the declared operator template. Without that the friend declaration would declare a non-template operator+ which is not defined, that's why you got the linker error.

    BTW: You can also specify the template argument explicitly like

    // refers to the instantiation of template operator+ for T 
    friend Stack<T> operator+<T>(Stack<T> a, Stack<T> b);
    //                       ^^^
    

    When using <> the template parameter T would be deduced from function parameter, and has the same effect.

    // refers to the instantiation of template operator+ for T 
    // T is deduced from function parameters
    friend Stack<T> operator+<>(Stack<T> a, Stack<T> b);
    //                       ^^