使用单个int的初始化列表时,int与std :: vector<int>的重载决议

3
为什么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? 
}

<source>:10:9: warning: braces around scalar initializer [-Wbraced-scalar-init]
    foo({0}); // calls foo(int) and issues a warning,
        ^~~

也许这个结果令人惊讶,因为编译器选择了一个选项,然后又发出一条诊断信息?

使用 Clang 14。

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


不完全是这样。你正在告诉构建一个可以从int构建的东西,并且可以将其提供给调用foo。 嗯,int就可以胜任这个角色。 - Lærne
对我来说并不意外。(顺便说一句,如果你真的把std::initializer_list<int>作为标题,它会获胜) - apple apple
@Lærne 但这不是 std::vector 的第10个构造函数吗?https://en.cppreference.com/w/cpp/container/vector/vector - Oliver Schönrock
为什么编译器在选择 int 时会选择 std::vector<int>,它可以在不创建任何中间类型的情况下调用 foo?强制使用 std::initializer_list 的规则仅适用于显式调用的函数/构造函数,而不适用于中间类型。否则,您可以争论任意创建中间类型,例如,它应该构建一个 std::array<int, 1> 来构建一个 std::deque<int> 来构建一个 std::vector<int> 来调用 foo - Lærne
1
@Giogre 这是因为你不能将临时变量绑定到左值引用 int& - apple apple
显示剩余10条评论
1个回答

5

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

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

我们需要参考[over.ics.list]/7.2,其中规定:

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

因此,对于该转换序列,我们有一个用户定义的转换。

进一步观察:

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

我们发现在[over.ics.list]/10.1中涵盖了转换,其中规定:

如果初始化列表只有一个元素,并且该元素本身不是初始化列表,则隐式转换序列就是将该元素转换为参数类型所需的序列;

在这种情况下,该元素是整型字面量0,这是一个精确匹配标准转换。

所以现在我们有了一个用户定义的转换和一个标准转换,这就是[over.ics.rank]/2.1所涵盖的内容。

标准转换序列优于用户定义的转换序列或省略号转换序列,而且

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


是的,那就是我想要的...谢谢,那让它变得清晰了。 - Oliver Schönrock
1
@OliverSchönrock 不用谢。重载解析是一个非常难以理解的主题。有时我仍然会对其中所有的角落案例感到惊讶。 - NathanOliver
有没有其他方法可以“鼓励”编译器……而不是将调用站点的参数类型更改为std::initializer_list<int> - Oliver Schönrock
2
@OliverSchönrock 噢,除了使用 std::initializer_list<int> 参数之外,我不知道还有什么方法,但是这比使用 std::vector 重载更好。标准委员会决定只有在没有其他重载有效时才将 { single_value } 视为列表,因为通常当您有一个列表时,您会有多个项目,而单个项目意味着您想要一个单个对象。例如,对于 auto foo = { 0 };foo 推断为 int,而不是 initializer_list<int>,但是对于 auto bar = { 1 , 42 };bar 是一个 std::initializer_list<int> - NathanOliver
是的,公平的。我能理解他们是如何得出那个结论的。这里可能有些不方便,但也算是公平的。谢谢。 - Oliver Schönrock
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接