2

我有以下代码:

class Foo;
class Bar;

class Bar {
public:
    Bar() {
    }

    Bar(Foo &foo) {
    }
};

class Foo {
public:
    Foo() {
    }
    Foo(Foo &foo) {
    }
    Foo(const Bar &bar) {
    }
};

Bar operator >> (const Bar &left, const Bar &right) { return Bar(); }

Foo a;
Foo b;
Foo c = a >> b;

在 Visual Studio 10 中,上面的代码编译得很好:编译器识别出Bar可以从 实例化Foo&,因此它调用适当operator >>的 ,然后返回一个Bar实例,并Foo(const Bar &)适当地调用构造函数。

但是,GCC 4.5 不编译上述代码。它输出以下错误:

error: no matching function for call to 'Foo::Foo(Foo)'
note: candidates are: Foo::Foo(const Bar&)
note:                 Foo::Foo(Foo&)
note:                 Foo::Foo()

根据语言标准,为什么会发生上述情况以及哪个编译器是正确的?

编辑:

为什么 C++由于存在 ,会创建一个临时Foo对象?c = a >> bFoo(const Bar &)

4

2 回答 2

4

这与重载或参数查找无关。通过定义Foo(Foo&),您已经禁用了默认复制构造函数的生成Foo(const Foo&),这是从右值初始化所必需c的。添加带有 Foo(const Foo&) 签名的 ctor,您的代码将运行良好。我不知道为什么 VS10 编译它,但我会尝试查找指定它为什么不应该发生的子句。

我们开始吧: §12.8(1) 指定类 X 的非模板构造函数是复制构造函数,如果它的第一个参数是 X&、const X&、volatile X& 或 const volatile X& 类型并且没有其他参数或所有其他参数有默认值。

§12.8(5) 说,如果我们不为 X 定义一个复制构造函数(以上述任何形式),编译器以 X(const X&) 的形式定义一个复制构造函数。

因此定义 Foo(Foo&) 定义了一个复制构造函数,因此编译器不能再隐式定义 Foo(const Foo&) 。

于 2011-06-15T13:10:36.600 回答
2

这应该有效:

Foo c(a >> b);

此初始化语法:

Foo c = a >> b;

被解释为

Foo c(Foo(a >> b));

复制初始化,需要一个可访问的复制构造函数,它接受const Foo&.


来自[dcl.init]标准部分(来自 C++0x 的措辞):

初始化的形式(使用括号或 =)通常是无关紧要的,但当初始化器或被初始化的实体具有类类型时,它就很重要;见下文。如果被初始化的实体没有类类型,则带括号的初始化器中的表达式列表应为单个表达式。

表单中发生的初始化

   T  x  =  a;

以及在参数传递、函数返回、抛出异常 (15.1)、处理异常 (15.3) 和聚合成员初始化 (8.5.1) 中称为复制初始化。[注意:复制初始化可能会调用移动(12.8)。——尾注]

表单中发生的初始化

   T  x(a);
   T  x{a};

以及在 new 表达式 (5.3.4)、static_cast 表达式 (5.2.9)、函数符号类型转换 (5.2.3) 以及基类和成员初始化器 (12.6.2) 中称为直接初始化

语法在 C++0x 中是新的T x{a},所有其他规则与 C++03 相同。


然后适用以下规则(同一部分):

  • 如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 非限定版本与目标类相同或派生类,则考虑构造函数。枚举了适用的构造函数(13.3.1.3),并通过重载决议(13.3)选择最佳构造函数。调用如此选择的构造函数来初始化对象,使用初始化表达式或表达式列表作为其参数。如果没有构造函数适用,或者重载决议不明确,则初始化格式错误。

  • 否则(即,对于剩余的复制初始化情况),可以从源类型转换到目标类型或(当使用转换函数时)到其派生类的用户定义转换序列被枚举,如 13.3 中所述。 1.4,最好的一个是通过重载决议(13.3)选择的。如果转换无法完成或不明确,则初始化格式错误。 以初始化表达式作为参数调用所选函数;如果函数是构造函数,则调用初始化目标类型的 cv 非限定版本的临时版本。临时是prvalue。然后根据上述规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象。 在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除这种直接初始化中固有的复制;见 12.2、12.8。

于 2011-06-15T13:33:47.837 回答