【问题标题】:Why isn't the call `A a(c);` in main() ambiguous?为什么 main() 中的调用 `A a(c);` 不模棱两可?
【发布时间】:2016-08-27 21:44:00
【问题描述】:

为什么在main() 中调用A a(c); 不明确?

struct C;

struct A {

    A() { std::cout << "default ctor A" << '\n'; }
    A(const A&) { std::cout << "copy A" << '\n'; }
    A(C&) { std::cout << "ctor A(C)" << '\n'; };
};
struct C {

    C() { std::cout << "default ctor C" << '\n'; }
    operator A() { std::cout << "C::operator A()" << '\n'; return A(); };
};


int main()
{
     C c;
     A a(c); 
}

代码打印(clang 和 GCC):

default ctor C
ctor A(C)

如果我们注释掉构造函数 A::A(C&),代码会打印:

default ctor C
C::operator A()
default ctor A
copy A
copy A

【问题讨论】:

  • 为什么应该它是“模棱两可的”?所谓的歧义在哪里?
  • 根据操作的优先级。
  • @WakeupBrazil 其中一个直接调用构造函数,另一个首先调用转换运算符,然后调用构造函数。一种操作优于两种操作也就不足为奇了。
  • 按照你的逻辑,将char 传递给void func(int); void func(char); 也应该是模棱两可的。
  • 一个是“精确匹配”,另一个是“转换”。前者是一个更好的重载。

标签: c++ initialization language-lawyer ambiguous c++17


【解决方案1】:

如果你有这个,重载解决过程的目标是确定如何将C 转换为A,那么它确实是模棱两可的,因为无论是转换构造函数或者可以使用转换函数:

void f(A);
f(c);

但是,这和那个不一样:

A a(c);

这里,重载解析用于确定调用A的哪个构造函数,因此它选择参数类型与实参类型完全匹配的构造函数。

标准引用是 C++14 中的 [over.match.ctor]/1:

当类类型的对象被直接初始化(8.5),或从相同或 派生类类型(8.5),重载决议选择构造函数。对于直接初始化,候选 函数是被初始化对象的类的所有构造函数。对于复制初始化, 候选函数是该类的所有转换构造函数(12.3.1)。参数列表是 初始化器的表达式列表赋值表达式

【讨论】:

  • 但是声明A a(c);中的表达式c不一样,也不是A类型的派生类。
  • @WakeupBrazil:你在说什么? A 有一个以C 为参数的构造函数。而C 与表达式c 的类型相同。
  • @NicolBolas 我的印象是 [over.match.ctor]/1 中的句子 from an expression of the same or a derived class type (8.5) 将适用于直接和复制初始化这两种情况。这解释了我上面的评论。但是现在,更仔细地查看引用的段落,我可以看到所引用的句子仅适用于复制初始化。
  • @NicolBolas 但我仍然不明白这一段如何解释A::A(C&amp;)C::operator A() 并不模棱两可。
  • @WakeupBrazil:“候选函数都是被初始化对象的类的构造函数”C::operator A() 不是A 的构造函数之一,因此它不是一组候选函数。所以没有歧义。
猜你喜欢
  • 2020-01-21
  • 2011-06-18
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
  • 2011-08-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多