为什么在调用不明确的ctor时没有编译时错误?

Why is no compile-time error when calling an ambiguous 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()).

令我惊讶的是:

为什么第二次调用没有触发重载解析歧义的编译时错误?

,一段类似的代码确实会引发歧义错误。

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

[over.match.list]

1 When objects of non-aggregate class type T are list-initialized ([dcl.init.list]), overload resolution selects the constructor in two phases:

  • Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations ([over.match.ctor], [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]

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

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