const 和 ref 限定成员函数的功能与非 const/ref 成员函数没有区别。这些标记主要是为了防止程序员滥用东西。
没有那个对象是 const 因为你不应该改变它,所以
你不能在上面调用变异函数!
如果你有一个函数void g() const,那么它不会突然编译成不同的指令,因为你从中删除了const - 这只是意味着编译器停止检查你是否在正文中改变了this。
嗯,大多数情况下......它变得有点复杂,因为通过支持各种不变量,这些关键字有时还允许程序员(或编译器)做一些在一般情况下不允许的事情。例如,如果我知道你给了我一个右值,那么我就可以窃取它的内部信息,而无需复制它们。
无论如何,为了说明,我通过godbolt 运行了您的三个示例。
在第一种情况下void f() 编译为:
S::f():
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
您不必能够阅读所有内容,只需看到当我编译其他两个函数时它们产生了这个:
S::f() &:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
S::f() &&:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
我们很清楚,除了加载不同的字符串文字(.LC0、.LC1)之外,它们三个都是相同的。如果我们添加-O3,那么它就会被内联到main 中:
没有引用限定符:
main:
sub rsp, 8
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
使用 ref 限定符:
main:
sub rsp, 8
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
这又是相同的(字符串文字除外)
简而言之:不需要做任何事情来让非引用限定函数接受两个参数 - 反之亦然:编译器阻止引用限定函数接受它们技术上可以 但不允许 使用(既要防止程序员做他们不应该做的事情,也可以防止函数被优化以执行在一般情况下实际上会中断的事情)。
在某种程度上,它就像类型系统:在机器级别上,计算机只是在位和字节之间移动,并且对于您认为任何给定块代表的类型绝对是零。纯粹是编译器试图让您承担责任并确保您支持您声明的不变量,但是一旦编译器满意并发出一些机器代码,那么这些类型早就不复存在了。
或者,因为我喜欢例子,你可以说这就像 VIP 和非 VIP 门之间的区别:门实际上是相同的,但是守卫(编译器)只允许你通过那些(他们想想)你有权限。