21

我有这个代码

struct A { A(); A(A&); }; 
struct B { B(const A&); }; 

void f(A); 
void f(B); 

int main() { 
   f(A()); 
}

令我惊讶的是,这在 GCC 和 Clang 中失败了。Clang 例如说

Compilation finished with errors:
source.cpp:8:10: error: no matching constructor for initialization of 'A'
       f(A()); 
         ^~~
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument
    struct A { A(); A(A&); }; 
                    ^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
    struct A { A(); A(A&); }; 
               ^
source.cpp:4:13: note: passing argument to parameter here
    void f(A); 

f当第二个工作正常时,他们为什么选择第一个f?如果我删除第一个f,则调用成功。对我来说更奇怪的是,如果我使用大括号初始化,它也可以正常工作

int main() { 
   f({A()}); 
}

他们都叫第二个f

4

1 回答 1

17

这是一个语言怪癖。第一个f匹配得更好,因为您A不需要转换来匹配参数类型 ( A),但是当编译器尝试进行调用时,找不到合适的复制构造函数会导致调用失败。在执行重载解决步骤时,该语言不允许考虑实际调用的可行性。

最接近的匹配标准引用 ISO/IEC 14882:2011 13.3.3.1.2 用户定义的转换序列 [over.ics.user]:

将类类型的表达式转换为相同的类类型被赋予精确匹配等级,而将类类型的表达式转换为该类型的基类的转换被赋予转换等级,尽管事实上复制/移动为这些情况调用构造函数(即,用户定义的转换函数)。

对于列表初始化案例,您可能需要查看:13.3.3.1.2 用户定义的转换序列 [over.ics.user]

当非聚合类类型 T 的对象被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:

— 最初,候选函数是类 T 的初始化器列表构造函数(8.5.4),参数列表由初始化器列表作为单个参数组成。

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

因为重载决议必须在每种情况下查看可行的构造函数,f(A)并且它必须拒绝f(B)尝试绑定但仍然可行的序列。A()A(A&)B(const A&)

于 2013-04-06T15:21:24.490 回答