【问题标题】:Non-static member functions with no ref-qualifier没有引用限定符的非静态成员函数
【发布时间】:2020-01-17 23:22:20
【问题描述】:

这是my previous post的后续行动

参考Non-static member functions

const、volatile 和 ref 限定的成员函数

一个非静态成员函数可以声明为没有引用限定符,... 在重载决议期间,非静态 cv 限定成员函数 X 类处理如下:

没有引用限定符:隐式对象参数具有左值类型 引用 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()中的源代码,我在每个非静态成员函数重载之上提供了相关的 cmets 基于引用限定符,突出显示如果仅启用该特定重载将出现的编译问题。

我的问题是,为了使非 ref 限定的非静态成员函数能够对调用它的对象的隐式类型不可知,幕后发生了什么?编译器是否以适当的重载介入?

欣赏你的想法。

【问题讨论】:

    标签: c++11 methods overloading non-static


    【解决方案1】:

    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 门之间的区别:门实际上是相同的,但是守卫(编译器)只允许你通过那些(他们想想)你有权限。

    【讨论】:

      猜你喜欢
      • 2021-12-03
      • 1970-01-01
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多