#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的构造函数中指定了“显式”。
我认为func(A a)
不应被视为{1,2,3}
的候选人。实际上,事实并非如此。如果我删除func(const vector<int>& a)
,则代码仍然会失败,而不是通过消除歧义而成功。
总而言之,在这段代码中,func(const vector<int>& a)
是{1,2,3}
的唯一可调用函数,因此没有歧义。
我的问题是..
C ++重载解析过程如何得出“模棱两可”的结论?
为什么C ++不只是简单地选择可调用对象?
答案 0 :(得分:1)
explicit
构造函数在执行列表初始化时不会被忽略。此类构造函数始终被视为可行的重载候选对象。发生的事情是,如果系统尝试在复制列表初始化下调用explicit
构造函数(即,在重载解析之后),则会遇到硬编译错误。
在您的情况下,由于重载设置不明确,因此永远无法解决问题。
explicit
并不意味着“如果您尝试进行转换就不存在”;它的意思是“如果尝试转换将出错”。 explicit
的要点是迫使用户考虑他们实际要使用的类型。这样做是为了防止用户编写对阅读器不太明确的代码。
答案 1 :(得分:1)
我相信c在这里是正确的。 C ++中的重载解析分三个阶段进行:首先,构造一组候选函数,这是调用可能引用的所有函数的集合(基本上,名称解析的所有函数的集合)。然后缩小候选函数的初始集合,以得出一组可行函数(可以使用给定参数进行调用的函数集)。最后,对可行功能进行排名以确定最佳可行功能。最好的可行功能就是最终被称为。
第三,为使
F
成为可行的函数,每个自变量都应存在一个隐式转换序列,该序列将自变量转换为F
的对应参数。 […]
当参数类型不是引用时,隐式转换序列会根据参数表达式对参数的复制初始化进行建模。 […]
由于必需的构造函数被标记为void func(A a)
,因此explicit
似乎没有这样的隐式转换序列(复制初始化将失败)。因此,该函数不是可行的函数,也不再用于重载解析,因此void func(const vector<int>& a)
成为唯一可行的候选函数,随后将被调用该函数。
此外,纯粹从概念上讲,似乎只有在我们实际知道要初始化的参数(即知道哪个参数)后,才能对参数的复制列表进行初始化。函数实际上将被调用。如果在每个潜在候选函数中只有一个参数不是对应参数的有效初始化程序的情况下,调用重载集是非法的,那么重载的意义何在?只要我们仍在努力找出要调用的函数,就无法确定初始化是否格式错误。铛完全表现出这种行为。当您注释掉void func(const std::vector<int>& a)
重载时,clang会突然抱怨该呼叫的格式不正确...
答案 2 :(得分:0)
我同意@Nicol Bolas。 MSVC和gcc是正确的,而clang和icc是错误的。
在重载解决方案中,列表初始化与复制初始化不同,复制初始化认为列表初始化会考虑使用显式构造函数,而复制初始化则不会。
(来自cppreference)
列表初始化当非聚合类类型T的对象为 列表初始化的两阶段过载解析。
在第1阶段,候选函数全部是initializer-list T和参数列表的构造函数,用于重载 如果重载,解析由单个初始化程序列表参数组成 在第1阶段进入解析失败,进入第2阶段 函数都是T的构造函数,并且是 解决过载的目的包括 初始化列表。如果初始化列表为空并且T具有一个 默认构造函数,阶段1被跳过。
在复制列表初始化中,如果阶段2选择了一个明确的 构造函数,初始化格式不正确(而不是全部 显式构造函数不均匀的复制初始化 已考虑)。
一些例子:
这个
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(int, int, int) {}
};
struct B
{
B(std::initializer_list<int>) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f({ 1,2,3 }); //list initialziation
}
这个
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(std::initializer_list<int>) {}
};
struct B
{
B(std::initializer_list<int>) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f({ 1,2,3 }); //Also list initialization
}
与此同时
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(int) {}
};
struct B
{
B(int) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f(1); //Copy initialization
}
在所有四个编译器上都成功。