以下示例不符合标准:
void f();
struct A {
A() {
return f(); // should be replaced to `f(); return;`
}
};
但是当构造函数被替换为返回 void 的函数时,这是合法的。
我知道这是标准要求的,如下:
12.1 构造函数
12不应为构造函数指定返回类型(甚至 void )。构造函数主体中的 return 语句不应指定返回值。
但为什么?
以下示例不符合标准:
void f();
struct A {
A() {
return f(); // should be replaced to `f(); return;`
}
};
但是当构造函数被替换为返回 void 的函数时,这是合法的。
我知道这是标准要求的,如下:
12.1 构造函数
12不应为构造函数指定返回类型(甚至 void )。构造函数主体中的 return 语句不应指定返回值。
但为什么?
你曾说过:
但是当构造函数被替换为返回 void 的函数时,这是合法的。
但是,我不相信它是:这是编译器资源管理器的链接,它表明您的代码无法编译。
我已经用x86-64 clang 10.0.1
,和 对此进行了测试x86-64 gcc 10.2
,x64 msvc v19.24
并且所有三个都无法编译。以下是他们各自报告的错误:
A
不能返回 void 表达式return
关键字下方。0
。A
构造函数不能返回值你的问题中的这个陈述似乎有点误导......我根本不明白它是如何合法的!
构造函数只能做三件事......
};
,控制流返回给调用者。所有函数都必须有一个返回类型,即使是那些不返回的,例如:
void print(){/*...*/ return;}
int add(int a, int b) { return (a+b); }
但是,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
};
没有与s 和type
s 相关联的s,至少在 中没有。ctor
dtor
c++
现在,如果您从Compiler Explorer(void)0
中删除代码中的 ,您将看到和标签及其堆栈帧。您还将看到标签及其堆栈帧。然而,您将一无所获。现在,如果您通过使用类对象的实例实例化它来添加 in main 的实例,您将在 main 的堆栈框架中看到汇编代码的更改,因为它是函数的本地。foo():
bar():
main:
A
A
main()
这是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 个编译器中看到的,当您没有对象实例时,不会生成汇编代码。当您确实有一个对象的实例时,所有编译器都会调用call
或A::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
a
的type
A
。
我希望这有助于澄清为什么constructor
s 或ctor
s 没有与之关联的返回类型。他们destructor
的 s 或dtor
s 也是如此。
编辑
我认为人们错过了解释我试图理解的内容......我对代码进行了轻微修改:也许这会更清楚地说明我的意图......
这是 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()
void
bar()