-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Description
Description:
When passing {1, 2.0}
to overloaded functions, compilers select the std::initializer_list<int>
overload and report a narrowing error. According to the standard, this overload should be excluded from consideration because not all elements can be implicitly converted to X due to narrowing.
Expected behavior:
The conversion to std::initializer_list<int>
should be considered invalid during overload resolution per §12.2.4.2.6/5. The overload f(A)
should be selected as the only viable candidate.
Actual behavior:
Clang 20.1.0 selects the initializer_list
overload and emits:
note: insert an explicit cast to silence this issue
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
Minimal Reproduction (Godbolt):
https://godbolt.org/z/fhrM5MYsq
#include <initializer_list>
struct A { int x; double y; };
void f(A) {} // #1
void f(std::initializer_list<int>) {} // #2
int main() {
f({1, 2.0}); // Should select #1 but selects #2 with error
}
Standard Reference (§12.2.4.2.6/5):
"Otherwise, if the parameter type is std::initializer_list and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X..."
Analysis:
- The conversion
double(2.0) → int
requires narrowing - Therefore, the condition "all elements can be implicitly converted to X" fails
- The
initializer_list<int>
overload should be excluded during overload resolution
Affected Standards:
C++11 through C++23 (all show same behavior)
Request:
Adjust overload resolution to properly exclude candidates where narrowing conversions prevent satisfying the "all elements implicitly convertible" requirement in [over.ics.list]p5.
Contrast with Explicit Conversion Behavior (Expected Model):
When replacing narrowing with explicit conversion - which should be excluded similarly - compilers correctly reject the candidate during overload resolution:
#include <initializer_list>
struct Number {
explicit operator int() const { return 0; } // 显式转换
};
struct A { int x; Number y; };
void f(A) {} // #1
void f(std::initializer_list<int>) {} // #2
int main() {
f({1, Number{}}); // Correctly selects #1 in all compilers
}
This proves:
- Compilers properly exclude
initializer_list
candidates when conversions are invalid (explicit) - Narrowing conversions should follow the same exclusion principle