15

The chapter 14.1 of "C++ primer 5th edition" reads,

An operator function must either be a member of a class or have at least one parameter of class type.

For example, string("hello")+"world" compiles "hello"+"world" doesn't. And when I want to overload the + on two C strings.

std::string operator+ (const char* s1, const char* s2)

I get the following error.

error: ‘std::string operator+(const char*, const char*)’ must have an argument of class or enumerated type

I have two questions.

  1. Is this restriction part of the language specification? If yes, why C++ designers want to do that?

  2. std::string has constructor like string (const char* s);, which implies that compiler can do implicit class-type conversion from char* to string. When we call "hello"+"world", why doesn't the compiler convert the two char* "s to two strings? After all, there is a overloading "+" on two std::strings. string operator+ (const string& lhs, const string& rhs);

Peng Zhang
  • 3,475
  • 4
  • 33
  • 41
  • 11
    that would create some "interesting" pointer arithmetics if you could overload addition of two pointers. – Anycorn May 06 '14 at 22:15
  • @Anycorn Brilliant. After I defined string_add (std::string s1, std::string s2), I can call "string_add("hello", "world")" with no problem. So, seems like there are implicit conversions as long as the function is not a operator overloading. – Peng Zhang May 06 '14 at 22:19
  • 3
    @PengZhang that's because std::string can be constructed from char*. The sequence is, construct s1 from `char*`, construct s2 from `char*` then pass those temporaries to your function. – W.B. May 06 '14 at 22:20
  • @W.B. There is an operator overloading "string operator+ (const string& lhs, const string& rhs);" Why doesn't the compiler convert the two C strings to std::strings and match the "operator +"? Function matching and type conversion, which one is executed first? – Peng Zhang May 06 '14 at 22:25
  • Imagine overloading `int operator + (int, int);` – Anthony May 06 '14 at 22:31
  • @anthony-arnold I think your comment is as good as Anycorn's. That kind of function definition would confuse the compiler, because pointers in C++ is an integer. – Peng Zhang May 06 '14 at 22:34
  • 1
    I believe it suffices if one type is not a built-in, e.g. an enum. – Kerrek SB May 06 '14 at 22:43

3 Answers3

11
  1. Is this restriction part of the language specification?

Yes, it is. Why? Well, the main reason is probably because redefining the usual operators for standard types is seen as obfuscating. Imagin overloading operator+(int,int) or operator+(int,char*). That would change the meaning of existing code!

You could argue that there are operators between standard types that are not defined, so you could safely override them, for example operator*(char*,int), but that is usually seen as rather useless.

Moreover, operator overloads must be global, (or be in the namespace of some of its members (argument dependent lookup), but with standard types there are no namespace to depend on), so interoperatibility between libraries that fancy to override them would be a nightmare.

  1. "hello" + "world": why doesn't the compiler convert the two char* "s to two strings?

Well, for one, std::operator+(const std::string&, const std::string&) is in namespace std; so it will not be found by default.

You could try calling the operator explicitly: std::operator+("hello", "world"), but alas, there are so many operator+() overloads, many of them templates, that the call is ambiguous.

So, taking all this into account, I think that a reasonable requirement is that at least one of the operators to be of a user-defined type. Think about it: the global and namespace problem is solved, ADL can be used (actually it was invented for this use) and it is impossible to redefine an operator with an existing meaning (except operator,() and operator&(), I think, but who wants to override these...).

rodrigo
  • 94,151
  • 12
  • 143
  • 190
8

As Bjarne said in Design and Evolution, the goal was to make it easy to extend the language, not to mutate it. If you allowed overloading only builtin types, you would change the language, and allowing that went against the design goals. (For example, it would encourage the formation of mutually incompatible dialects and fracture the community.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • AFAICT, this is a good reason to avoid allowing the overload of _already defined_ semantics like `T* operator+(T*, ptrdiff_t)` and its complement `T* operator+(ptrdiff_t, T*)` but I have my doubts if it is a good reason to forbid the overload of _previously undefined_ operators like `string operator+(const char*, const char*)` – Massa May 06 '14 at 22:50
  • @Massa: Well, there's a previous rule that says that the expression `a + b` is ill-formed if both `a` and `b` are pointers, so you'd be mutating *that* rule. – Kerrek SB May 06 '14 at 22:53
  • 3
    @Massa: Also, this may be a good example of how the C++ design solves your problem: You can write `"abc"s + "def"s` to get a string. Instead of changing existing semantics, the judicious addition of a new feature (user-defined literals) offers a neat solution. – Kerrek SB May 06 '14 at 22:55
  • @KerrekSB, I'm still kind of waiting on `"abc"sv`, and a `string_view` class to go with it I suppose :p – chris May 06 '14 at 23:06
  • @chris they are working ok in `g++-4.9`! It's taking `clang++-3.5`'s place for me! :D – Massa May 07 '14 at 00:23
  • @Massa, Surely you mean libstdc++. You can still use Clang with libstdc++ (or libc++). It seems like libc++'s is in the works. – chris May 07 '14 at 00:35
  • But `clang++-3.5` does not accept the `sv` suffix **even if I put it in a system header** (with `libc++`) or with `libstdc++`... Just like that. (ah, and the current `` in Sid's `g++-4.9` has a bug... some `nothrow` declarations missing.) – Massa May 07 '14 at 00:40
  • 1
    @Massa: None of this is finalized yet, so inconsistencies and errors in current implementations are not surprising. Give it some time (or file bugs on the the Clang bug tracker?). – Kerrek SB May 17 '14 at 19:22
6

Is this restriction part of the language specification? If yes, why C++ designers want to do that?

Yes, it is. Quoting from N3337, §13.5.6 [over.oper]:

An operator function shall either be a non-static member function or be a non-member function and have at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.

About the "why" part, because removing this requirement would mean that you could overload operators for builtin types, which already have their operator semantics defined. That's something totally undesiderable.

For instance, do you think that defining this makes any sense?

int operator+(int a, int b) { return a - b; }

Does it allow more people to reason about your program when they read your code, or is it just something surprising that effectively breaks your reasoning?

What happens if we get pointers into the game?

bool operator==(const char *str1, const char *str2) { 
    return strcmp(str1, str2) == 0;
}

Would you expect that operator== now dereferences memory? I wouldn't. It's surprising. It goes against what the Standard already says about these operators. It breaks what every C program in the last 25 years behaved like. The language shouldn't let you do this kind of abuses.

peppe
  • 21,934
  • 4
  • 55
  • 70