关于运算符重载和参数相关查找,Visual Studio 10 和 GCC 4.5 之间的哪个编译器是正确的?

发布于 2024-11-15 22:29:20 字数 984 浏览 8 评论 0原文

我有以下代码:

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& 实例化,因此它调用适当的 < code>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++ 由于 c = a >>> 创建一个临时 Foo 对象b,既然 Foo(const Bar &) 存在?

I have the following code:

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;

In Visual Studio 10, the above code compiles fine: the compiler recognizes that Bar can be instantiated from Foo&, and therefore it invokes the appropriate operator >>, which then returns a Bar instance, and the constructor Foo(const Bar &) is appropriately invoked.

However, GCC 4.5 does not compile the above code. It outputs the following error:

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

Why does the above happen and which compiler is correct, according to the language standard?

EDIT:

Why does C++ create a temporary Foo object as a result of c = a >> b, since Foo(const Bar &) exists?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱,才寂寞 2024-11-22 22:29:20

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

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

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

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

This has nothing to do with overloading or argument lookup. By defining Foo(Foo&), you've disabled the generation of the default copy constructor Foo(const Foo&), which is required to initialize c from an rvalue. Add a ctor with the Foo(const Foo&) signature and your code will run fine. I don't know why VS10 compiles this, but I will try to look up the clauses which specify why it shouldn't happen.

Here we go:
§12.8(1) specifies that a non template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X& and there are no other parameters or all other parameters have default values.

§12.8(5) says, that if we don't define a copy constructor for X (in any of the above mentioned forms), the compiler defines a copy constructor in the form X(const X&).

Thus defining Foo(Foo&) defines a copy constructor, so the compiler can't implicitly define Foo(const Foo&) anymore.

写给空气的情书 2024-11-22 22:29:20

这应该有效:

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);
Tx{a};

以及 new 表达式 (5.3.4)、static_cast 表达式 (5.2.9)、函数符号类型转换 (5.2.3) 以及基类和成员初始值设定项 (12.6.2) 被称为直接-初始化。

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


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

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

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

This should work:

Foo c(a >> b);

This initialization syntax:

Foo c = a >> b;

Is interpreted as

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

is copy-initialization and requires an accessible copy-constructor which accepts const Foo&.


From section [dcl.init] of the standard (wording from C++0x):

The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below. If the entity being initialized does not have class type, the expression-list in a parenthesized initializer shall be a single expression.

The initialization that occurs in the form

   T  x  =  a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). — end note ]

The initialization that occurs in the forms

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

as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.

The T x{a} syntax is new in C++0x, all the other rules are unchanged from C++03.


Then the following rules apply (same section):

  • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

  • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文