Jan Schultke
Jan Schultke

Reputation: 39558

Why can't most defaulted operators have a placeholder return type, i.e. auto?

C++20 has made it possible to default comparison operators, including the three-way comparison like this. <=> can have a deduced return type, but other operators can't:

struct S {
    friend auto operator<=>(const S&, const S&) = default; // OK
    friend auto operator==(const S&, const S&) = default;  // error, return type must be bool
    auto& operator=(const S&) = default;                   // error, return type must be S&
};

Is there any reason why this restriction exists? It feels arbitrary. The only return type that auto can reasonably deduce to for operator== is bool.


Note: This is a wiki-style Q&A and its main goal is to raise awareness for restrictive language design and a proposal which aims to solve it.

Upvotes: 8

Views: 132

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39558

The restriction is purely historical and there is a good chance that it's going to be lifted in C++26, assuming P2952R0 auto& operator=(X&&) = default gets accepted into the standard.

From the proposal:

auto& operator=(const MyClass& rhs) { i = rhs.i; return *this; } // well-formed
auto& operator=(const MyClass& rhs) = default; // ill-formed, must say 'MyClass&'

auto operator==(const MyClass& rhs) const { return i == rhs.i; } // well-formed
auto operator==(const MyClass& rhs) const = default; // ill-formed, must say 'bool'

The ill-formedness of these declarations comes from overly restrictive wording in the standard, such as [class.eq]/1 specifically requiring that a defaulted equality operator must have a declared return type of bool, instead of simply specifying that its return type must be bool. We believe each of the examples above has an intuitively clear meaning: the placeholder return type correctly matches the type which the defaulted body will actually return. We propose to loosen the current restrictions and permit these declarations to be well-formed.

In essence, the proposal would make deduced return types valid in those cases where a specific return type is expected. It would not alter the set of valid return types.

Implementation challenges and edge cases

It's worth noting that there is some compiler divergence when it comes to the set of valid return types. For example:

// only clang rejects this, other compilers allow it
friend bool operator==(const C, const C) = default;

However, such divergence shouldn't stand in the way of the proposal because it proposes the "canonical" return type:

A defaulted secondary comparison operator function shall have the return type bool. If its declared return type contains a placeholder type, its return type is deduced as if from return true;.

Upvotes: 6

Related Questions