1

Consider such code:

template<typename S>
class C;

template<typename S>
C<S> operator-(C<S> lhs, C<S> rhs);

template<typename S>
class C
{
public:
    C operator-() { return *this; }
    friend C operator-<S>(C lhs, C rhs);//error on this line
};

template<typename S>
C<S> operator-(C<S> lhs, C<S> rhs) { return C<S>(); }

int main(int argc, char** argv)
{
    C<int> a,b;
    a-b;
    return 0;
}

This gives me 6 errors in MSVC. But if I move the definition of C operator-() after the friend declaration, it compiles. If I change the class to an untemplated class, it compiles. And this seems to compiles in g++ too. (I have no g++ installed, based on https://wandbox.org/)

So what's wrong here?

user
  • 475
  • 3
  • 10
  • Why do you have `<>` as part of the friend? `friend C operator-<>(C lhs, C rhs);` – Martin York Sep 12 '19 at 17:06
  • @MartinYork That's a template function; have edited to make more clear – user Sep 12 '19 at 17:09
  • Yes I can see that. But its not needed as part of the friend declaration. Template resolution comes at the point where you are binding a specific template and then checking accesses. At the friend declaration point you don't need to do this. IF you want to do it then you need to have an explicit template just before the friend (and you only want that if you want to allow other C types to have friend accesses). – Martin York Sep 12 '19 at 17:13
  • 1
    @MartinYork I'm not familiar with templates, but if I omit `<>`, I get an unresolved external symbol; if I use `template<> friend C operator-(C lhs, C rhs);` I get 9 errors. btw, "not needed" means it's no harmful to include them, am I right? – user Sep 12 '19 at 17:21

2 Answers2

0

I have found some useful information data here. The problem is that the member operator unary minus hides the global binary subtract. So I have to declare the friend with

friend C<S> (::operator-<S>)(C<S> lhs, C<S> rhs);

to explicitly ask for operator- in the global scope: in c++ name lookup happens before type check.

Besides, as the friend is not part of the class (I suppose that's the reason), it is a must to write template argument after the return type and parameter type. Without them (i.e. friend C (::operator-<S>)(C lhs, C rhs);) it still works in MSVC, but not with g++.

user
  • 475
  • 3
  • 10
-1

This line is messing your code:

C operator-() { return *this; }

Remove it and your code compiles (well, change a+b to a-b).

It tries to overload an arithmetic operator with no operands. Arithmetic operators have one operand if declared inside a class, or two operands if declared outside.

From operator overloading doc:

It is not possible to change the precedence, grouping, or number of operands of operators.

The friend declaration, despite of being declared inside the class is not a member function of the class.

For much more info about operator oveloading see this question

Ripi2
  • 7,031
  • 1
  • 17
  • 33
  • "Arithmetic operators have one operand if declared inside a class, or two operands if declared outside" - not necessarily: there are also unary plus, unary minus and bitwise not (not counting increment and decrement operators); they have zero arguments if declared inside a class, one argument if declared outside. `C operator- ()` is, as far I understand, the unary minus declared inside a class. – max66 Sep 12 '19 at 18:48
  • "The friend declaration, despite of being declared inside the class is not a member function of the class." - and, correctly, the OP declare it with **two** arguments. – max66 Sep 12 '19 at 18:50
  • @Ripi2 max66 is right. I'm defining an unary - and declaring a friend binary -. – user Sep 12 '19 at 23:41