C++推导本

参考
大家知道,成员函数都有隐式对象参数,对非静态成员函数,它就是this指针;

struct S_implicit {
    void foo() {}
};

struct S_explicit {
    void foo(this S_explicit&) {}//显式写出来.
};

1:消除修饰带来的冗余,示例:

//之前
struct S_implicit {
    int data_;
    int& foo() & { return data_; }
    const int& foo() const& { return data_; }
};

// 之后.
struct S_explicit {
    int data_;

    template <class Self>
    auto&& foo(this Self& self) {
        return std::forward<Self>(self).data_;
    }//转发数据了.
};

消除了&,const&,&&,const&&等逻辑.借助推导本,只需编写一个版本即可.
这里使用了模板形式参数,一般,建议按显式对象参数名使用Self.
借助推导本,可实现递归λ.

int main() {
    auto gcd = [](this auto self, int a, int b) -> int {
        return b == 0 ? a : self(b, a % b);
    };

    std::cout << gcd(20, 30) << "\n";
}

这可再次增强λ函数.比如,借助推导本,可简化CRTP.

//之前
// CRTP
template <class Derived>
struct Base {
    void foo() {
        auto& self = *static_cast<Derived*>(this);
        self.bar();
    }
};

struct Derived : Base<Derived> {
    void bar() const {
        std::cout << "CRTP Derived\n";
    }
};

 之后,推导本
struct Base {
    template <class Self>
    void foo(this Self& self) {
        self.bar();
    }
};

struct Derived : Base {
    void bar() const {
        std::cout << "Deducing this Derived\n";
    }
};

该新实现CRTP,可省去CR,甚至是T,要更加自然,更加清晰.新定制点示例:

namespace mylib {

    struct S {
        auto abstract_interface(this auto& self, int param) {
            self.concrete_algo1(self.concrete_algo2(param));
        }
    };
} // mylib空间

namespace userspace {
    struct M : mylib::S {
        auto concrete_algo1(int val) {}
        auto concrete_algo2(int val) const {
            return val * 6;
        }
    };
} // 

int main() {
    using userspace::M;
    M m;
    m.abstract_interface(4);
}

这样,依然是静态多态,但代码更加清晰,无侵入,并支持显式可选,值得使用.
定制点,这里

推导本还可用来解决根据闭包类型的完美转发λ抓参数的问题.
亦即,如果λ函数的类型为左值,那么抓的参数就以左值转发;如果为右值,那么就以右值转发.示例:

#include <iostream>
#include <type_traits>
#include <utility> // for std::forward_like

auto get_message() {
    return 42;
}

struct Scheduler {
    auto submit(auto&& m) {
        std::cout << std::boolalpha;
        std::cout << std::is_lvalue_reference<decltype(m)>::value << "\n";
        std::cout << std::is_rvalue_reference<decltype(m)>::value << "\n";
        return m;
    }
};

int main() {
    Scheduler scheduler;
    auto callback = [m=get_message(), &scheduler](this auto&& self) -> bool {
        return scheduler.submit(std::forward_like<decltype(self)>(m));
    };
    callback(); // retry(callback)
    std::move(callback)(); // try-or-fail(rvalue)
}

// Output:
// true
// false
// false
// true

若是没有推导本,则无法简单完成该操作.

另一个是可按值形式传递this,对小对象,可提高性能.示例:

struct S {
    int data_;
    int foo(); // 隐式
    // int foo(this S); // 按值传递
};

int main() {
    S s{42};
    return s.foo();
}

// 隐式`本`生成的汇编代码:
// sub     rsp, 40               ;00000028H
// lea     rcx, QWORD PTR s$[rsp]
// mov     DWORD PTR s$[rsp], 42 ;0000002aH
// call    int S::foo(void)      ;S::foo
// add     rsp, 40               ;00000028H
// ret     0

// 按值传递生成的汇编代码:
// mov     ecx, 42      ; 0000002aH
// jmp     static int S::foo(this S); S::foo

对隐式this指针,生成汇编代码需要先分配栈空间,保存this指针到rcx寄存器中,再赋值42data_中,然后调用foo(),最后平栈.
而按值传递this,则无需那些操作,因为值传递的this不会影响s变量,可优化掉中间步骤,不再需要分配和平栈操作,所以可直接保存42到寄存器,再jmpfoo()处执行.

posted @   zjh6  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示