1

I have a class, let's call it Wrapper, that wraps a given type, let's say MyClass. In reality, Wrapper is a class template, but I think that's not relevant here.

Wrapper exposes the wrapped MyClass by means of a conversion operator (just for reading).

When I create an operator for MyClass as free function (in my example a unary minus operator), this works as expected, I can use the operator also on the Wrapper class.

If I, however, implement the operator as a member function, the compiler complains: error: no match for ‘operator-’.

I thought the free function and the member function are equivalent in this case, why aren't they?

Is there a way to change the Wrapper class that a member operator or MyClass works?

If there isn't, does this suggest that it is in general preferable to implement an operator as free function instead of as member function?

Here's some code to illustrate the problem.

struct MyClass {
  // This doesn't work:
  void operator-() const {}
};

// It works with this instead of the member operator:
//void operator-(const MyClass&) {}

struct Wrapper {
  operator MyClass() const { return MyClass(); }
};

int main() {
  -Wrapper();
}

Here's a live example: http://ideone.com/nY6JzR

Matthias
  • 4,524
  • 2
  • 31
  • 50
  • 1
    Similar: http://stackoverflow.com/questions/4622330/operator-overloading-member-function-vs-non-member-function – Csq Jun 03 '14 at 16:24
  • A conversion will be only executed, if you are requiring with a cast, or have a type that can be deduced as `MyClass` at the left side of an assignment. – πάντα ῥεῖ Jun 03 '14 at 16:27

2 Answers2

1

Question I thought the free function and the member function are equivalent in this case, why aren't they?

Answer

In order for -Wrapper() to work correctly, the compiler has to perform a lookup for operator-. It looks for the name operator- in the default namespace, in the class Wrapper, and the namespace where Wrapper is defined. It doesn't look at other classes and namespaces for the name.

That explains the compiler error when the operator-() is moved to MyClass and why it succeeds when it is defined as a non-member function.

Question Is there a way to change the Wrapper class that a member operator or MyClass works?

Answer There are two ways to resolve this.

  1. Make the operator functions for MyClass non-member functions.

  2. Create operator functions in MyClass as well as Wrapper, with the implementations in Wrapper being simple pass through functions. The good thing about this is that then you don't have to care whether the operator functions in MyClass are member functions or non-member functions.

Question If there isn't, does this suggest that it is in general preferable to implement an operator as free function instead of as member function?

Answer This can be a policy decision based on the choice you make to the previous answer.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • It looks for the name `operator-`. – Columbo Jun 03 '14 at 16:56
  • In my concrete use case, I prefer to keep the `Wrapper` as simple and general as possible (as it's in reality a class template), therefore I'll change `MyClass` as suggested in option 1. – Matthias Jun 04 '14 at 10:55
1

I thought the free function and the member function are equivalent in this case, why aren't they?

Lets see what happens when a conversion operator is used.

Essentially, there is a global operator function, which has a parameter of type MyClass. Now, since Wrapper has a conversion operator, the call operator-( Wrapper() ) will be inspected by overload resolution to find the best match - and overload resolution finds that there is a global operator function with parameter MyClass const& (or similiar). Then it tries to convert the Wrapper prvalue to MyClass const& and sees that there is a user-defined conversion sequence: One that uses the conversion operator to convert the Wrapper-object to a MyClass object, which then can be used to (copy-)initialize the reference.

If the operator function is a member instead, there is a problem: A global function call of the form operator-( Wrapper() ) does obviously not yield any viable functions. But the one that assumes the operator function is a member doesn't yield anything either - because operator- is not a member of Wrapper, therefore Wrapper().operator-() doesn't work and doesn't yield any viable functions either.

Remember: A class with a conversion operator does not inherit the members of the target type. If you wanted that, you should have inherited MyClass.

For a unary operator @ with an operand of a type whose cv-unqualified version is T1 [...] three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows:

  • If T1 is a complete class type, the set of member candidates is the result of the qualified lookup of T1::operator@ (13.3.1.1.1); otherwise, the set of member candidates is empty.
  • The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2) except that all member functions are ignored. [...]
Columbo
  • 60,038
  • 8
  • 155
  • 203