This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-10-26
Consider:
void f() noexcept {}
void g(void (*)() noexcept) {}
void g(void (&)()) {}
int main() {
g(f); // error: ambiguous
}
In contrast:
void f() noexcept {} void g(void (*)()) {} void g(void (&)()) {} // #1 int main() { g(f); // OK, calls #1 }
In both cases, binding void(&)() to void() noexcept is considered an identity conversion, without further disambiguation by 12.2.4.3 [over.ics.rank].
CWG 2024-06-26
Binding a reference to a function should not be considered an identity conversion if it strips a non-throwing exception specification. This amendment removes the ambiguity for the first example and makes the second example ambiguous, which is desirable.
Proposed resolution (approved by CWG 2024-10-11):
Change in 12.2.4.2.5 [over.ics.ref] paragraph 1 as follows:
When a parameter of type “reference to cvT” binds directly (9.4.4 [dcl.init.ref]) to an argument expression:[Example 1:
- If the argument expression has a type that is a derived class of the parameter type, the implicit conversion sequence is a derived-to-base conversion (12.2.4.2 [over.best.ics]).
- Otherwise,
if T is a function type, orif the type of the argument is possibly cv-qualified T, or if T is an array type of unknown bound with element type U and the argument has an array type of known bound whose element type is possibly cv-qualified U, the implicit conversion sequence is the identity conversion.[Note 1: When T is a function type, the type of the argument can differ only by the presence of noexcept. —end note]- Otherwise, if T is a function type, the implicit conversion sequence is a function pointer conversion.
- Otherwise, the implicit conversion sequence is a qualification conversion.
struct A {}; struct B : public A {} b; int f(A&); int f(B&); int i = f(b); // calls f(B&), an exact match, rather than f(A&), a conversion
void g() noexcept; int h(void (&)() noexcept); // #1 int h(void (&)()); // #2 int j = h(g); // calls #1, an exact match, rather than #2, a function pointer conversion—end example]
Change in 12.2.4.3 [over.ics.rank] bullet 3.2.6 as follows:
int f(const int &); int f(int &); int g(const int &); int g(int); int i; int j = f(i); // calls f(int &) int k = g(i); // ambiguous struct X { void f() const; void f(); }; void g(const X& a, X b) { a.f(); // calls X::f() const b.f(); // calls X::f() } int h1(int (&)[]); int h1(int (&)[1]);int h2(void (&)()); int h2(void (&)() noexcept);void g2() { int a[1]; h1(a);extern void f2() noexcept; h2(f2);}