我有几个与C++中的重载解析有关的问题。 请考虑以下示例:
extern "C" int printf (const char*, ...);
struct X {};
template <typename T>
struct A
{
A() = default;
template <typename U>
A(A<U>&&)
{printf("%s \n", __PRETTY_FUNCTION__);}
};
template <typename T>
struct B : A<T>
{
B() = default;
template <typename U>
operator A<U>()
{printf("%s \n", __PRETTY_FUNCTION__); return {};}
};
int main ()
{
A<X> a1 (B<int>{});
}
如果我用g++-std=C++11a.cpp
编译它,将调用a
的构造函数:
A<T>::A(A<U>&&) [with U = int; T = X]
如果我用g++-std=C++17a.cpp
编译程序,它将生成
B<T>::operator A<U>() [with U = X; T = int]
如果我将a(a&)
注释出来,并再次用g++-std=C++11a.cpp
编译它,则将调用转换运算符:
B<T>::operator A<U>() [with U = X; T = int]
否则,如果初始化是直接初始化,或者是复制初始化,其中源类型的CV非限定版本与目标类型的类相同,或者是目标类型的派生类,则考虑构造函数。 枚举适用的构造函数(16.3.1.3),通过重载解析(16.3)选择最佳的构造函数。 调用这样选择的构造函数来初始化对象,使用初始值设定项表达式或表达式列表作为参数。 如果没有应用构造函数,或者重载解析不明确,则初始化格式不正确。
a
的构造函数是更好的选择?b
的转换运算符似乎是更好的匹配,因为它不需要从b
到a
的隐式转换。附注。 有人知道我在哪里可以找到详细的指南,描述转换运算符如何参与重载解析,即当发生不同类型的初始化时,它们与构造函数交互的方式。 我知道标准提供了最准确的描述,但似乎我对标准措辞的解释与它的正确含义没有多少共同之处。 一些经验法则和其他示例可能会有帮助。
我相信这是公开的
考虑这样一个例子:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
这是11.6[dcl.init]项目符号17.6.2:[...]
重载解析选择cat
的移动构造函数。 初始化构造函数的cat&&
参数会产生一个临时值,参见11.6.3[dcl.init.ref]项目符号5.2.1.2。 这就排除了这种情况下复制删除的可能性。
这似乎是保证复制删除的措辞变化中的一个疏忽。 在这种情况下,我们应该同时考虑构造函数和转换函数,就像在复制初始化时一样,但我们需要确保这不会引入任何新的问题或歧义。