0

以下示例不符合标准:

void f();

struct A {
    A() { 
        return f(); // should be replaced to `f(); return;`
    }
};

但是当构造函数被替换为返回 void 的函数时,这是合法的。

我知道这是标准要求的,如下:

12.1 构造函数

12不应为构造函数指定返回类型(甚至 void )。构造函数主体中的 return 语句不应指定返回值。

但为什么?

4

2 回答 2

0

构造函数是一种特殊方法,旨在初始化该类的新实例。在引擎盖下它们实际上并没有被调用(参见这个问题),所以实际上任何返回都是不准确的,因为实际上没有返回任何内容。这里没有什么与 void 不同的,因为void它是一种类型,并且在不返回的代码块中返回一个类型将是令人困惑和误导的语法。

此外,构造函数作为初始化的一部分被调用,并且仅以与 write 相同的方式将参数的值写入内存部分int n = 5,将值 5 写入使用时引用的内存块n

对用户来说,初始化过程似乎只是一个函数调用,但实际上是一个完全不同的过程。

于 2020-08-23T03:22:06.937 回答
-2

你曾说过:

但是当构造函数被替换为返回 void 的函数时,这是合法的。

但是,我不相信它是:这是编译器资源管理器的链接,它表明您的代码无法编译。

我已经用x86-64 clang 10.0.1,和 对此进行了测试x86-64 gcc 10.2x64 msvc v19.24并且所有三个都无法编译。以下是他们各自报告的错误:

  • clang: -错误:构造函数A不能返回 void 表达式
  • 突出显示在return关键字下方。
  • gcc: -错误:从构造函数返回一个值
  • 突出显示在 value 之下0
  • msvc: -错误 C2534:A构造函数不能返回值
  • 其中突出显示在该构造函数的整个代码行下方。

你的问题中的这个陈述似乎有点误导......我根本不明白它是如何合法的!

构造函数只能做三件事......

  • 提前归还
  • 抛出异常
  • 在构造函数的块或范围结束后返回};,控制流返回给调用者。

所有函数都必须有一个返回类型,即使是那些不返回的,例如:

  • void print(){/*...*/ return;}
  • int add(int a, int b) { return (a+b); }
  • ETC...

但是,constructors并且destructors永远不会用type.

你永远不会看到:

struct A{
    void A() { return; }  // Fails to compile
    int A() { return 0; } // Fails to compile
    void ~A() { return; } // Fails to compile
    int ~A() {return 0; } // Fails to compile
};

没有与stypes 相关联的s,至少在 中没有。ctordtorc++

现在,如果您从Compiler Explorer(void)0中删除代码中的 ,您将看到和标签及其堆栈帧。您还将看到标签及其堆栈帧。然而,您将一无所获。现在,如果您通过使用类对象的实例实例化它来添加 in main 的实例,您将在 main 的堆栈框架中看到汇编代码的更改,因为它是函数的本地。foo():bar():main:AAmain()

这是clang's组装:

没有A a被宣布...

foo():                                # @foo()
        push    rbp
        mov     rbp, rsp
        pop     rbp
        ret
bar():                                # @bar()
        push    rbp
        mov     rbp, rsp
        xor     eax, eax
        pop     rbp
        ret
main:                                   # @main
        push    rbp
        mov     rbp, rsp
        xor     eax, eax
        mov     dword ptr [rbp - 4], 0
        pop     rbp
        ret

随着A a;被宣布...

foo():                                # @foo()
        push    rbp
        mov     rbp, rsp
        pop     rbp
        ret
bar():                                # @bar()
        push    rbp
        mov     rbp, rsp
        xor     eax, eax
        pop     rbp
        ret
main:                                   # @main
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 4], 0
        lea     rdi, [rbp - 8]
        call    A::A() [base object constructor]
        xor     eax, eax
        add     rsp, 16
        pop     rbp
        ret
A::A() [base object constructor]:                              # @A::A() [base object constructor]
        push    rbp
        mov     rbp, rsp
        mov     qword ptr [rbp - 8], rdi
        pop     rbp
        ret

这是gcc的组装:

没有:

foo():
        push    rbp
        mov     rbp, rsp
        nop
        pop     rbp
        ret
bar():
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret

和:

foo():
        push    rbp
        mov     rbp, rsp
        nop
        pop     rbp
        ret
bar():
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret
A::A() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rax, [rbp-1]
        mov     rdi, rax
        call    A::A() [complete object constructor]
        mov     eax, 0
        leave
        ret

msvc是组装:

没有:

void foo(void) PROC                                        ; foo
        ret     0
void foo(void) ENDP                                        ; foo

int bar(void) PROC                                        ; bar
        xor     eax, eax
        ret     0
int bar(void) ENDP                                        ; bar

main    PROC
        xor     eax, eax
        ret     0
main    ENDP

和:

void foo(void) PROC                                        ; foo
        ret     0
void foo(void) ENDP                                        ; foo

int bar(void) PROC                                        ; bar
        xor     eax, eax
        ret     0
int bar(void) ENDP                                        ; bar

this$ = 8
A::A(void) PROC                                  ; A::A, COMDAT
        mov     QWORD PTR [rsp+8], rcx
        mov     rax, QWORD PTR this$[rsp]
        ret     0
A::A(void) ENDP                                  ; A::A

a$ = 32
main    PROC
$LN3:
        sub     rsp, 56                             ; 00000038H
        lea     rcx, QWORD PTR a$[rsp]
        call    A::A(void)                     ; A::A
        xor     eax, eax
        add     rsp, 56                             ; 00000038H
        ret     0
main    ENDP

正如您从所有 3 个编译器中看到的,当您没有对象实例时,不会生成汇编代码。当您确实有一个对象的实例时,所有编译器都会调用callA::A()......A::A(void)执行控制就像一个函数一样进入这些构造函数,但是,它们没有类型,因为它们不是实际函数,它们只是被当作一个......

它们确实有一个堆栈帧、一个作用域和一个像函数一样的生命周期,但是这些只有在声明class's 或struct' 类型的对象时才会被调用。构造函数的汇编指令class仅在创建实例时生成。

它们不像您可以执行此操作的常规功能:

foo() {
    A(); // this is not valid
    A::A(); // this is not valid
} 

但是,这是有效的:

foo() {
    A a(); // Valid
}

这里constructor是在属于的对象上调用named atype A

我希望这有助于澄清为什么constructors 或ctors 没有与之关联的返回类型。他们destructor的 s 或dtors 也是如此。


编辑

我认为人们错过了解释我试图理解的内容......我对代码进行了轻微修改:也许这会更清楚地说明我的意图......

这是 C++ 代码:

struct A {
    //A() { return (void)0; }
    A() {};
};

void foo() {
    A a;
    return (void)0;
}

int bar() {
    A a;
    return 0;
}

int main() {
    foo();
    int baz = bar();
    A a;

    return 0;
}

GCC's是它的版本Assembly

foo():                                # @foo()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rdi, [rbp - 8]
        call    A::A() [base object constructor]
        add     rsp, 16
        pop     rbp
        ret
A::A() [base object constructor]:                              # @A::A() [base object constructor]
        push    rbp
        mov     rbp, rsp
        mov     qword ptr [rbp - 8], rdi
        pop     rbp
        ret
bar():                                # @bar()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rdi, [rbp - 8]
        call    A::A() [base object constructor]
        xor     eax, eax
        add     rsp, 16
        pop     rbp
        ret
main:                                   # @main
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 4], 0
        call    foo()
        call    bar()
        mov     dword ptr [rbp - 8], eax
        lea     rdi, [rbp - 16]
        call    A::A() [base object constructor]
        xor     eax, eax
        add     rsp, 16
        pop     rbp
        ret

这是Compiler Explorer的更新链接。如果您仔细查看生成的程序集,当被调用时,没有任何信息,也没有任何关于类型的程序集代码。当被调用时,它的返回类型被优化掉,当被调用时,有汇编代码来存储它的返回值。A::A()foo()voidbar()

于 2020-08-23T04:01:28.717 回答