使用单个 int 的初始值设定项列表进行 int 与 std::vector<int> 的重载解析

Oli*_*ock 3 c++ overloading initializer-list language-lawyer overload-resolution

为什么 C++ 选择原始类型重载匹配而不是“更好”的匹配初始值设定项列表?


#include <vector>

void foo([[maybe_unused]] int i) {}

void foo([[maybe_unused]] const std::vector<int>& v) {}

int main() {
    foo(0);
    foo({1,2,3});
    foo({0}); // calls foo(int) and issues a warning,
              // rather than what seems like the "better"
              // match foo(vector).. why? 
}
Run Code Online (Sandbox Code Playgroud)
<source>:10:9: warning: braces around scalar initializer [-Wbraced-scalar-init]
    foo({0}); // calls foo(int) and issues a warning,
        ^~~
Run Code Online (Sandbox Code Playgroud)

也许是“令人惊讶”的结果,因为编译器选择了随后发出诊断的选项?

使用 Clang 14

https://godbolt.org/z/1dscc5hM4

Nat*_*ica 5

{0}没有类型,所以我们需要尝试将其转换为重载集的参数类型。当考虑

void foo([[maybe_unused]] const std::vector<int>& v) {}
Run Code Online (Sandbox Code Playgroud)

我们需要查阅[over.ics.list]/7.2,其中指出

否则,隐式转换序列是用户定义的转换序列,其第二标准转换序列是恒等转换。

所以我们有一个用户定义的转换序列。

看着

void foo([[maybe_unused]] int i) {}
Run Code Online (Sandbox Code Playgroud)

我们发现[over.ics.list]/10.1中涵盖了转换,其中指出

如果初始值设定项列表中有一个元素本身不是初始值设定项列表,则隐式转换序列是将元素转换为参数类型所需的序列;

本例中的元素是0,它是一个整数文字,它是精确匹配标准转换

现在我们有了用户定义的转换与标准转换,并且[over.ics.rank]/2.1涵盖了这一点

标准转换序列是比用户定义的转换序列或省略号转换序列更好的转换序列,并且

现在我们知道标准转换是更好的转换,这就是int选择重载而不是std::vector<int>重载的原因。

  • @OliverSchönrock 啊,据我所知,除了使用 `std::initializer_list&lt;int&gt;` 参数之外,这甚至比 `std::vector` 重载更好。标准委员会决定仅在没有其他有效重载的情况下将“{ single_value }”视为列表,通常当您有一个列表时,您有多个项目,而单个项目意味着您需要一个对象。例如,使用 `auto foo = { 0 };` `foo` 被推导为 `int`,而不是 `initializer_list&lt;int&gt;`,但是使用 `auto bar = { 1 , 42 };` `bar` 是一个 `std` ::initializer_list&lt;int&gt;`。 (2认同)