Is it allowed to overload operator+ for a standard library type and a built-in type?

Viewed 633

Is it allowed to overload operators, such as operator+ for a combination of a standard library type and a built-in type, when such an overload doesn't exist?

For example, is it legal to implement the following operator in the default namespace or a user-defined namespace:

std::string operator+(const std::string& s, int right) { ... }

I know there are various restrictions about implementing things in the std:: namespace, but it isn't clear to me if there is any rule against the above (whether it's a good idea is of course an entirely different matter!).

3 Answers

For example, is it legal to implement the following operator in the default namespace or a user-defined namespace:

std::string operator+(const std::string& s, int right) { ... }

Yes, it's perfectly legal to do this. The only restrictions are about adding names to namespace std or specializing member function function templates or class templates or adding deduction guides for class templates in std.

There's nothing that stops you from writing something like:

namespace N {
    std::string operator+(std::string s, int ) { return s; }
}

That is a well-formed program, per the standard. However note that since by definition your operators will not have any program-defined types in them, they will never be found by ADL. Since they are operators, which typically are found by ADL, this in of itself may be a reason to avoid such a pattern:

namespace U {
    auto foo() {
        return "hello"s + 1; // error: name lookup doesn't find our operator
    }

    auto bar() {
        using namespace N;
        return "hello"s + 1; // ok: for some definition of ok
    }
}

It is legal to write that. But it won't necessarily do what you want.

If your goal is to make some_string + 4 legal C++ code, then its legality will depend on exactly where that expression appears. Operator overloading will use ADL to look up the operator [over.match.oper]/3.2:

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 (6.4.2) except that all member functions are ignored. However, if no operand has a class type, only those non-member functions in the lookup set that have a first parameter of type T1 or “reference to cv T1 ”, when T1 is an enumeration type, or (if there is a right operand) a second parameter of type T2 or “reference to cv T2”, when T2 is an enumeration type, are candidate functions.

But since your operator+ is not in the same namespace as either argument, ADL lookup will fail. And the list of non-member candidates will not automatically include global functions, there's no chance for it to find the right operator+.

So you cannot effectively overload an operator like this without opening up the namespace. Which of course is forbidden for std.

It's perfectly OK to overload operator+ between std::string and int. The only restriction that the standard places on that is (https://timsong-cpp.github.io/cppwp/n3337/over.oper#6):

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.

There is no restriction on the being able to define them in your namespace for even the types from the standard library.

Related