以下示例不符合标准:
void f();
struct A {
A() {
return f(); // should be replaced to `f(); return;`
}
};
但是,当构造函数替换为返回void的函数时,这是合法的。
我知道这是标准所必需的,如下:
12.1构造函数
12不得为构造函数指定返回类型(甚至无效)。构造函数主体中的return语句不得指定返回值。
但是为什么?
答案 0 :(得分:0)
constructor是一种特殊的方法,旨在初始化该类的新实例。在幕后并没有实际调用它们(请参阅this question),因此实际上任何返回都将是不准确的,因为实际上没有返回任何内容。这里的内容与void没什么不同,因为void
是一种类型,并且在不返回的代码块中返回类型会造成语法混乱和误导。
此外,构造函数被称为initialization的一部分,并且仅以写入int n = 5
的相同方式将参数的值写入内存部分,将值5写入内存块,即使用n
时引用。
对于用户而言,初始化过程似乎只是一个函数调用,而实际上却是一个完全不同的过程。
答案 1 :(得分:-2)
您说过:
但是,当构造函数替换为返回void的函数时,这是合法的。
但是,我不相信这是:这是 Compiler Explorer 的链接,它表明您的代码无法编译。
我已经使用x86-64 clang 10.0.1
,x86-64 gcc 10.2
和x64 msvc v19.24
对此进行了测试,但全部三个都无法编译。以下是它们各自报告的错误:
A
不能返回空表达式 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
};
有{strong> NO 个type
与ctor
和dtor
相关,至少在c++
中没有。
现在,如果您从 Compiler Explorer 中删除代码中的(void)0
,您将看到foo():
和bar():
标签及其堆栈框架。您还将看到main:
标签及其堆栈框架。然而,A
您什么都看不到。现在,如果通过使用类对象的实例实例化实例来在main中添加A
的实例,您将看到main的堆栈框架中汇编代码的更改,因为它是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
或struct
类型的对象时才调用它们。 class
构造函数的汇编指令仅在创建实例时生成。
它们不像常规函数,您可以在其中执行以下操作:
foo() {
A(); // this is not valid
A::A(); // this is not valid
}
但是,这是有效的:
foo() {
A a(); // Valid
}
在constructor
named
中a
type
的对象上调用A
。
我希望这有助于阐明为什么constructor
或ctor
没有与之关联的返回类型。他们的destructor
或dtor
也是如此。
修改
我认为人们很想解释我想要得到的东西...我对代码做了些微修改:也许这可以更清楚地说明我的意图...
这是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
的{{1}}版本:
Assembly
这是指向 Compiler Explorer 的udpated链接。而且,如果仔细查看生成的程序集,则在调用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
时,将没有信息,也没有任何类型的程序集代码。调用A::A()
时,其返回类型foo()
已被优化,而调用void
时,将使用汇编代码存储其返回值。