C++23新特性_Deducing this

文章目录

本文讲解C++23新特性之Deducing this.

第一章 C++23语言特性

1.1 Deducing this

在C++20之前,一个类的this指针隐士藏在这个类的成员函数的第一个参数中。在类中提供一个获取属性的方法get(),为了支持左值,const左值,右值等不同场景,实现如下:

cpp 复制代码
    struct Container {
        std::string val;

        // 1. 可变左值
        std::string& get()& { return val; }
        /*编译器视角下的this
            std::string& get(Container* const this) 
            {
                return this->val;
            }
        */

        // 2. const 左值
        const std::string& get() const& { return val; }
        /*编译器视角下的this
            std::string& get(const Container* const this)
            {
                return this->val;
            }
        */

        // 3. 右值 (移动语义)
        std::string&& get()&& { return std::move(val); }
        /*
            std::string&& get(Container* const this) 
            {
                // 这里的 val 实际上等价于 static_cast<std::string&&>(this->val)
                return std::move(this->val); 
            }
        */
    };

    void test() {
        Container c;
        c.get() = "World";       // 调用 std::string& get()& -> 返回 string&
		std::cout << c.get() << endl; // 输出 World

        const Container cc;
        std::cout << cc.get() << endl;;   // 调用 const std::string& get() 

        std::string s = Container{}.get(); // 
        std::cout << Container{}.get() << endl; //调用 std::string&& get()&&
    }

在编译器看来,这3个get()也是函数重载,因为每一个get()都隐藏了this指针,编译器根据不同的this指针生成3个函数符号,调用时,从3个符号中选择最优的一个调用。

上边写法的写法实际上有些重复,有没有更好的实现方式呢?

在C++23之前的奇异递归模板模式,为了在基类中使用派生类的方法,不得不使用CRTP,这需要static_cast转换,这个转换实际上一个this指针调整。这种CRTP方式,代码还是比较晦涩难懂,涉及到继承,this指针转换等。CRTP请看这个博客:
https://blog.csdn.net/weixin_43916755/article/details/155103708?sharetype=blogdetail&sharerId=155103708&sharerefer=PC&sharesource=weixin_43916755&spm=1011.2480.3001.8118

C++23为了解决this问题,允许显示声明对象参数,通过万能引用模板推导参数的类型,解决重写书写问题。

1.1.1 Decucing this 语法格式

在成员函数的第一个参数前加上 this 关键字。

在成员函数中,第一个参数加上 this.

cpp 复制代码
    struct X
    {
        // 传统写法
        void foo()
        {
			// this 指针隐式转换为 X*
        }

		// C++23 写法
        void bar(this X& self) //
        {
			//  self是一个名字,类型是 X&
		}

        // 最强模板写法
        template<typename T>
        void baz(this T&& self)
        {
            // self 可以是任何类型的引用
		}
};

1.1.2 应用场景

示例1:改进get

将上面3个get使用Deducing this合并为1个。

cpp 复制代码
    struct Container2 {
        std::string val = "Hello";

        // 一个函数统治所有
        template <typename Self>
        auto&& get(this Self&& self) {
            // 转发 self (即转发 *this)
            // 如果 self 是 const&,返回 const&
            // 如果 self 是 &&,返回 && (移动)
            return std::forward<Self>(self).val;
        }
    };
    void test()
    {
        Container2 c;
		c.get() = "World";       // 调用 get(),返回 string&
		cout << c.get() << endl; // 输出 World

        const Container2 cc;
        cout << cc.get() << endl;   // 调用 get(),返回 const string&

        std::string s = Container2{}.get(); // 调用 get(),返回 string&&
		cout << Container2{}.get() << endl; // 输出 Hello
    }
示例2:递归Lambda

在 C++23 之前,Lambda 表达式很难直接递归调用自己(因为在 Lambda 内部无法引用自身类型)。通常需要借助 std::function(有性能开销)或复杂的 Y 组合子。

现在通过Deducing this,Lambda可以显示捕获自己。

cpp 复制代码
    void test()
    {
        auto fib = [](this auto&& self,int n)
            {
                if (n < 2)
                {
                    return n;
                }
                return self(n - 1) + self(n - 2);
            };

		std::cout << "Fibonacci(10): " << fib(10) << std::endl;
        // Fibonacci(10): 55
    }
示例3:实现简化版的CRTP

复杂版的CRTP如下:

cpp 复制代码
    template<typename T>
    class Base
    {
    public:
		// 在基类中调用派生类的方法
        void handleDerived()
        {
            T& derived = static_cast<T&>(*this);// 派生类对象也是基类对象,静态转换
            derived.myfunc();
        }

    private:
        Base() {}
        friend T; // 友元 
    };
    class Derived1 : public Base<Derived1> // 普通类
    {
    public:
        void myfunc()
        {
            cout << "Derived1::myfunc()执行了" << endl;
        }
    };

    template <typename T>
    class Derived2 : public Base<Derived2<T>>
    {
    public:
        void myfunc()
        {
            cout << "Derived2::myfunc()执行了" << endl;
        }
    };

    class Derived3 : public Base<Derived3>
    {
		// 未实现 myfunc(),尝试调用会导致编译错误
    };
    void test()
    {
        Derived1 myde;
        myde.handleDerived();
        // 输出:Derived1::myfunc()执行了

        Derived2<int> myde2;
        myde2.handleDerived();
        // Derived2::myfunc()执行了

		Derived3 myde3;
		//myde3.handleDerived(); 
        // 编译错误:Derived3 未实现 myfunc()
    }

具体讲解,见上面博客链接。

简化版的如下,可以看到避免了T 类型的static_cast转换。

cpp 复制代码
    class Base
    {
    public:
        template<typename Self>
        void handleDerived(this Self&& self)
        {
			self.myfunc();
        }
    };

    class Derived1 : public Base
	{
    public:
        void myfunc()
        {
            std::cout << "Derived::myfunc() called" << std::endl;
        }
    };
    class Derived2 : public Base
	{
        public:
        void myfunc()
        {
            std::cout << "Derived2::myfunc() called" << std::endl;
        }
	};
    void test()
    {
        Derived1 d;
        d.handleDerived(); // 调用基类模板方法
        // 输出: Derived::myfunc() called
		Derived2 d2;
        d2.handleDerived(); // 调用基类模板方法
		// 输出: Derived2::myfunc() called
    }

1.1.3 总结

Deducing this 是C++这门古老语言迈向现代化的又一重要步伐。主要特性总结如下:

1 去重:它用一个模板函数替代了 const/non-const/lvalue/rvalue 的四个重载。

2 解耦:它让 CRTP 模式不再依赖复杂的模板继承语法,实现了真正的"鸭子类型"混入。

3 赋能:它让 Lambda 递归变得轻而易举。

相关推荐
Mr_WangAndy2 小时前
C++23新特性_if consteval
c++·c++23·c++40周年·if consteval
Mr_WangAndy1 天前
C++20新特性_std::jthread和chrono库扩展
c++20·c++20新特性·c++40周年·chrono库扩展·jthread线程
Mr_WangAndy1 天前
C++20新特性_原子智能指针,std::source_location和位操作函数
c++20·c++40周年·c++原子智能指针·source_location·位操作函数
Mr_WangAndy1 天前
C++20新特性_[[likely]] , [[unlikely]]属性和特性测试宏
c++20·likely·c++40周年·unlikely·特性测试宏
Mr_WangAndy1 天前
C++20新特性_std::format和span
c++20·format·c++20新特性·span·c++40周年
Mr_WangAndy2 天前
C++20新特性_概念 (Concepts)
c++20·c++20新特性·c++40周年·c++20概念
Mr_WangAndy2 天前
C++20新特性_范围 (Ranges)
c++20·c++20新特性·c++40周年·范围ranges·视图适配器·视图view
Mr_WangAndy2 天前
C++20新特性_[[no_unique_address]]属性
c++20·c++20新特性·c++40周年
Mr_WangAndy2 天前
C++20新特性_模块(Modules)
c++20·c++40周年·c++20新特性模块