关于initializer_list的函数重载的解析

问题描述 投票:4回答:2
#include <vector>
using namespace std;

class A
{
public:
    explicit A(const initializer_list<int> & a) {}
};
void func(const vector<int>& a)
{

}
void func(A a)
{

}
int main(void)
{
    func({ 1,2,3 });
}

此代码无法编译:

(19):错误C2668:'func':对重载函数的模糊调用

(13):注意:可能是'void func(A)'

(9):注意:或'void func(const std :: vector>&)'与[_Ty = int](19):注意:尝试匹配参数列表'(初始化列表)'

请注意,我在A的构造函数上指定了'explicit'。

在我看来,func(A a)不应被视为{1,2,3}的候选人。实际上,事实并非如此。如果我删除func(const vector<int>& a),那么代码仍然失败,而不是成功消除歧义。

总之,在这段代码中,func(const vector<int>& a){1,2,3}唯一可调用的函数,所以没有歧义。

我的问题是......

  1. C ++重载解析程序如何得出“模糊”的结论?
  2. 为什么C ++不只是简单地选择可调用的?
c++ c++11
2个回答
1
投票

执行列表初始化时,不会忽略explicit构造函数。这种构造函数总是被认为是可行的超载候选者。如果系统试图在复制列表初始化下调用explicit构造函数(即:在重载解析之后),则会发生硬编译错误。

在你的情况下,它永远不会那么远,因为重载集是模糊的。

explicit并不意味着“如果你试图改变就不存在”;它意味着“如果你试图转换错误”。 explicit的目的是强迫用户思考他们实际想要使用的类型。它可以防止用户编写对读者有些模棱两可的代码。


1
投票

我相信铿锵在这里是正确的。 C ++中的重载分辨率分三个阶段进行:首先构造一组候选函数,它是调用可能引用的所有函数的集合(基本上是名称解析所选择的所有函数的集合)。然后缩小这个初始候选函数集以得到一组可行的函数(可以使用给定参数调用的函数集)。最后,对可行功能进行排序以确定最佳可行功能。这个最好的可行功能最终将被称为。

来自[over.match.viable]/4

第三,为了使F成为一个可行的函数,每个参数都应该存在一个隐式转换序列,它将该参数转换为F的相应参数。 [...]

特别是基于[over.best.ics]/6

当参数类型不是引用时,隐式转换序列模拟参数表达式中参数的复制初始化。 [...]

由于必要的构造函数被标记为void func(A a)(复制初始化将失败),似乎没有这样的explicit隐式转换序列。因此,该函数不是一个可行的函数,并且不再被认为是重载解析,这使得void func(const vector<int>& a)成为唯一可行的候选者,这是将被调用的函数。

而且,纯粹在概念层面上,一旦我们真正知道我们要初始化哪个参数,即知道哪个函数实际上是错误的,参数的复制列表初始化似乎是错误的。将被称为。如果对每个潜在候选函数中的相应参数的单个参数不是有效的初始化器,那么对重载集的调用将是非法的,那么重载的重点是什么?只要我们仍在努力确定要调用哪个函数,就无法确定初始化是否会形成错误。 clang正好表现出这种行为。当你注释掉void func(const std::vector<int>& a)超载时,clang会突然抱怨说这个电话是不正确的...

try it out here

© www.soinside.com 2019 - 2024. All rights reserved.