当前位置: 首页 > 知识库问答 >
问题:

无ref限定符的非静态成员函数

黄信厚
2023-03-14

这是我上一篇文章的后续文章

参考非静态成员函数

在下面

const-、vole-和ref限定的成员函数

非静态成员函数可以在没有ref限定符的情况下声明,...在重载解析期间,X类的非静态cv限定成员函数被处理如下:

无ref限定符:隐式对象参数具有对cv限定X的类型左值引用,并且还允许绑定右值隐式对象参数

为了进一步探索这一点,我试用了上面链接中提供的源代码,如下所示:

#include <utility>
#include <iostream>
using std::move;
using std::cout;
using std::endl;

struct S {
    void f() {cout << "no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified S and is additionally allowed to bind rvalue implied object argument\n"; }

    //if only the below method signature were to be enabled,
    //the invocations using rvalue implicit object would fail
    //to compile with the error [-fpermissive]
    //void f() & {cout << "lvalue\n"; }

    //if only the below method signature were to be enabled,
    //the invocation using lvalue implicit object would fail
    //to complile with the error [-fpermissive]
    //void f() && {cout << "rvalue\n"; }
};

int main (void){

    S s;
    s.f();       // prints "lvalue"
    move(s).f(); // prints "rvalue"
    S().f();          // prints "rvalue"

    return 0;
}

我已经在每个基于引用限定符的非静态成员函数重载上方提供了相关注释,强调了如果仅启用该特定重载,那么根据main()中的源代码就会出现的编译问题。

我的问题是,为了使非ref限定的非静态成员函数能够与调用它的对象的隐式类型无关,在兜帽下发生了什么?编译器是否介入适当的重载?

欣赏你的想法。

共有1个答案

高宸
2023-03-14

const和ref限定的成员函数与非const/ref成员函数的功能没有区别。这些标记主要是为了防止程序员滥用东西。

不,那个对象是const,因为你不应该改变它,所以你不允许调用它的突变函数!

  • 编译器对程序员大喊大叫

如果您有一个函数ulug()const,那么它不会突然编译为不同的指令,因为您从中删除了const-这只是意味着编译器停止检查如果您在正文中突变this

嗯,主要是。。。更复杂的是,通过支持各种不变量,这些关键字有时还允许程序员(或编译器)执行一般情况下不允许的操作。例如,如果我知道你给了我一个右值,那么我就可以偷走它的内脏,而不需要复制它们。

无论如何,为了举例说明,我通过godbolt运行了您的三个示例。

在第一种情况下,编译为:

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中:

无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:.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

这也是相同的(字符串文字除外)

简而言之:不必做任何事情来让非ref限定函数同时接受这两个参数,相反:编译器会阻止ref限定函数接受它们在技术上可以接受但不允许接受的参数(两者都是为了防止程序员做他们不应该做的事情,如果函数被优化为做一些在一般情况下实际上会中断的事情)。

在某种程度上,它就像类型系统:在机器级别上,计算机只是在比特和字节之间移动,对于任何给定的块所代表的类型,都不会给出任何他妈的结果。纯粹是编译器试图让您承担责任,并确保您维护您声明的不变量,但一旦编译器满意并发出一些机器代码,那么类型就早已消失。

或者,因为我喜欢例子,你可以说这就像贵宾和非贵宾门之间的区别:门实际上是相同的,但是警卫(编译器)只允许你通过(他们认为)你有权限。

 类似资料:
  • 如果没有其他重载(比如,

  • 根据文件,上面说 在重载解析期间,类X的非静态cv限定成员函数被视为一个函数,如果它没有ref限定符或具有左值ref限定符,则该函数将左值引用类型的隐式参数带到cv限定X。否则(如果它有rvalue ref限定符),它将被视为一个函数,将rvalue reference类型的隐式参数作为cv限定的X。 问题: 没有任何ref限定符的成员函数如何?它们将被视为使用左值限定符隐式声明的函数。我说得对吗

  • 在类中,static 除了可以声明 静态成员变量,还可以声明静态成员函数。普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。 编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 th

  • 本文向大家介绍C++中静态成员函数与静态成员变量(static ),包括了C++中静态成员函数与静态成员变量(static )的使用技巧和注意事项,需要的朋友参考一下 C++中静态成员函数与静态成员变量(static ) 这篇介绍了静态成员函数与静态成员变量,是我的读书笔记,我希望它够简短但又比较全面,起到复习的作用。如果有一些C++知识记不清楚了,它可以帮你很快回忆起来。 复习C语言的stati

  • 本文向大家介绍C ++中的静态成员函数,包括了C ++中的静态成员函数的使用技巧和注意事项,需要的朋友参考一下 在上一篇文章中,我们讨论了C ++中的静态数据成员,并讨论了可以通过成员函数访问静态数据成员,但是该函数应该是静态成员函数 静态成员函数是一种特殊的成员函数,仅用于访问静态数据成员,其他任何常规数据成员均无法通过静态成员函数访问。就像静态数据成员一样,静态成员函数也是类函数。它不与任何类

  • 本文向大家介绍PHP静态成员变量和非静态成员变量详解,包括了PHP静态成员变量和非静态成员变量详解的使用技巧和注意事项,需要的朋友参考一下 数据成员可以分静态变量、非静态变量两种. 静态成员:静态类中的成员加入static修饰符,即是静态成员.可以直接使用类名+静态成员名访问此静态成员,因为静态成员存在于内存,非静态成员需要实例化才会分配内存,所以静态成员不能访问非静态的成员..因为静态成员存在于