首页 > 解决方案 > 为什么调用模棱两可的ctor时没有编译时错误?

问题描述

#include <iostream>
#include <vector>

int main()
{
    auto v1 = std::vector<std::size_t>(std::size_t{8});
    std::cout << v1.size() << std::endl;

    auto v2 = std::vector<std::size_t>{std::size_t{8}};
    std::cout << v2.size() << std::endl;
}

代码输出:

8
1

我知道这是 C++ 中的一个众所周知的问题,因为:

std::vector<std::size_t>(std::size_t{8})来电

explicit vector(size_type count)尽管

std::vector<std::size_t>{std::size_t{8}}来电

vector(std::initializer_list<T> init, const Allocator& alloc = Allocator()).

出乎我的意料:

为什么第二次调用不会因重载解析歧义而触发编译时错误?

在另一个相关问题中,一段类似的代码确实会触发歧义错误。

标签: c++c++11initializer-listambiguousoverload-resolution

解决方案


因为没有歧义会导致错误。当我们使用列表初始化时,重载解决方案明显不同。它是故意分两个阶段完成的。

[over.match.list]

1当非聚合类类型 T 的对象被列表初始化([dcl.init.list])时,重载决议分两个阶段选择构造函数:

  • 最初,候选函数是类 T 的初始化列表构造函数([dcl.init.list]),参数列表由初始化列表作为单个参数组成。

  • 如果找不到可行的初始化列表构造函数,则再次执行重载决议,其中候选函数是类 T 的所有构造函数,参数列表由初始化列表的元素组成。

如果初始化列表没有元素并且 T 有默认构造函数,则省略第一阶段。在复制列表初始化中,如果选择了显式构造函数,则初始化格式错误。[注意:这与其他情况([over.match.ctor],[over.match.copy])不同,其中仅考虑转换构造函数进行复制初始化。仅当此初始化是重载决议的最终结果的一部分时,此限制才适用。——尾注]

第一步 std::initializer_list考虑构造函数。我们可以到达第二步,只有在第一次重载决议失败时才考虑其他构造函数。显然,重载决议不会找不到合适的std::initializer_list构造函数。

您链接到的问题不是关于初始化向量的歧义。歧义在于选择函数重载。两者都是可行的,因为它们都接受一个不同的向量,该向量本身可以从同一个初始化列表中明确地初始化。


推荐阅读