c++23中的新功能之十六std::forward_like

一、介绍

前面说过,c++标准其实是分成两块推进的。一块是语言标准,另外一块是应用库标准。线程同步还麻烦呢,别说两个组的大佬,不同步的现象肯定会出现。在老的标准里还比较少,
在c++11以后经常发现,后续版本会对前面的版本打补丁,其实好多就是因为这种情况。
在前面分析过很多通过完美转发来实现的例子,比如才分析过的单例。在c++11以后,使用完美转发加上右值引用几乎可以用来常见的参数处理和转发控制(当然还是有一些是有问题的)。
但是这里会有一个应用场景出现(总有但是),如果想转发类型内部成员怎么办?类似于下面:

struct Data
{int d;
};
void getData(int d){}
template <typename T>
void send(T && t)
{getData(std::forward<T>(t).d);
}

代码跑起来很正常。但这个代码可不可以扩展呢?比如在Data内部有指针,有自定义类,有STL中的容器?可以试一下,用std::vector或者自己写一个类。程序跑起来仍然没有什么问题,但这就没问题了吗?

二、std::forward_like

在上一节提出了问题,首先回答这个问题要先回到完美转发的目的来。std::forward的目的是在模板编程时能够保持原参数的类型和值类型状态(左或右值等),从而准确的传递参数。这其实在c++非泛型编程中可以窥探出它的效果。举一个简单的例子,有一个函数接收int参数,但传入short也是没有问题的。原因是c++的隐式转换,这种转换很常见,但风险也非常大。最典型的就是有符号类型和无符号类型的转变时的溢出问题。它在非泛型编程中,还是比较容易发现和查找的,但是在泛型编程,也就是模板中,很难发现。同时,在后期的类型萃取中也会导致异常。等等还有其它一些问题,都是完美转发被广泛应用的一个原因。
所以,这时回到原来的问题上,对于普通(基础)类型或者自定义的一些普通类对象,做为右值仍然会被传递成为右值,这个不会有什么问题。但对于指针和容器或者自定义的一些特殊情况的类型,就有问题了。
std::forwardstd::vector中,标准库对[]有两个重载即只区分了常量和非常量。也就是说vector::[]const返回一个左值常量,可move(t)(也就是完美转发)后,move(t).vec[id]仍然是一个左值。它不是一个右值,也就是完美转发的过程中虽然把const传递了下去,但是左右值丢失了。
那么利用forward(t).容器内容,这种情况,c限定符保留了下来,但左右值失去了,那么使用std::forward的目的也失去了。而指针更甚一层,它的处理只是被传回一个非常量的左值(除非原来指针指向就是一个常量)。这样有没有完美转发,意义已经不存在了。
在自定义类中,如果稍微复杂一些,一定可能包含上述的情况,那么,完美转发的意义就大大失色了。c++的大佬们当然不会坐视这个问题,于是提出了std::forward_like:

getData(std::forward_like<T>(t.d));
//如果含有指针:
getData(std::forward_like<T>(*t.ptr));

它的形式和std::forward有一些不同,可以理解成直接完美转发成员了。

三、和Deducing This共用

在前面分析过Deducing This,std::forward_like可以和这个属性共用,用来转发自己的成员:

template<typename T>
struct Data
{T* value;template<typename Owner>decltype(auto) operator*(this Owner&& owner){ return std::forward_like<Owner>(*owner.value); }
};

再看一个常用的lambda表达式应用:

template <typename F>
auto check(F&& f) {return [f = std::forward<F>(f)](this auto&& owner)noexcept(!std::invoke(std::forward_like<decltype(owner)>(f)));
}

这里的就可以从模板参数的起始来完美转发相关的常量和左右值了。此处的noexcept使用到了其对表达式的处理方式,即noexcept(expression),noexcept和noexcept(true)等价,表示不抛出异常,为false时表示可能抛出异常。在新的标准里throw()这种方式已经被抛弃。

三、例程

再看一个cppreference上的例子:

#include <cstddef>
#include <iostream>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>struct TypeTeller
{void operator()(this auto&& self){using SelfType = decltype(self);using UnrefSelfType = std::remove_reference_t<SelfType>;if constexpr (std::is_lvalue_reference_v<SelfType>){if constexpr (std::is_const_v<UnrefSelfType>)std::cout << "const lvalue\n";elsestd::cout << "mutable lvalue\n";}else{if constexpr (std::is_const_v<UnrefSelfType>)std::cout << "const rvalue\n";elsestd::cout << "mutable rvalue\n";}}
};struct FarStates
{std::unique_ptr<TypeTeller> ptr;std::optional<TypeTeller> opt;std::vector<TypeTeller> container;auto&& from_opt(this auto&& self){return std::forward_like<decltype(self)>(self.opt.value());// It is OK to use std::forward<decltype(self)>(self).opt.value(),// because std::optional provides suitable accessors.}auto&& operator[](this auto&& self, std::size_t i){return std::forward_like<decltype(self)>(container.at(i));// It is not so good to use std::forward<decltype(self)>(self)[i], because// containers do not provide rvalue subscript access, although they could.}auto&& from_ptr(this auto&& self){if (!self.ptr)throw std::bad_optional_access{};return std::forward_like<decltype(self)>(*self.ptr);// It is not good to use *std::forward<decltype(self)>(self).ptr, because// std::unique_ptr<TypeTeller> always dereferences to a non-const lvalue.}
};int main()
{FarStates my_state{.ptr{std::make_unique<TypeTeller>()},.opt{std::in_place, TypeTeller{} },.container{std::vector<TypeTeller>(1)},};my_state.from_ptr();my_state.from_opt();my_state[0]();std::cout << '\n';std::as_const(my_state).from_ptr();std::as_const(my_state).from_opt();std::as_const(my_state)[0]();std::cout << '\n';std::move(my_state).from_ptr();std::move(my_state).from_opt();std::move(my_state)[0]();std::cout << '\n';std::move(std::as_const(my_state)).from_ptr();std::move(std::as_const(my_state)).from_opt();std::move(std::as_const(my_state))[0]();std::cout << '\n';
}

运行结果:

mutable lvalue
mutable lvalue
mutable lvalueconst lvalue
const lvalue
const lvaluemutable rvalue
mutable rvalue
mutable rvalueconst rvalue
const rvalue
const rvalue

注意,编译器需要支持。

四、总结

大家一起来打补丁吧。估计大佬们的心里也是有多少匹马在奔腾,但奔腾向何方,这个只有他们自己知道。快有快的好,慢有慢的好。哪个才是最合适的,只有试试才知道。然后,大佬们说:试试就试试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/146189.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue 实现数字验证码功能

需求&#xff1a;写了一个 手机发送验证码后 输入固定验证码的功能 封装成一个组件,如下: <template><div class"conts"><div class"box"><div class"code_list"><div :class"[ code_item, hideIndex 0 ? co…

Flutter笔记:滚动之-无限滚动与动态加载的实现

Flutter笔记 无限滚动与动态加载的实现 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/133342307 本文还…

分享10个必备的VS Code技巧和窍门,提高你的开发效率

目录 前言 1. 时间线视图&#xff1a;本地源代码控制 2. 自动保存&#xff1a;不再需要按Ctrl S 3. 使用命令面板进行任何操作 4、快速转到文件 5. 快速跳转指定行 6. 快速删除该行 7. 享受使用流畅的光标进行打字 8. 快速格式化代码 9. 使用多光标编辑功能节省时间…

【智能家居项目】裸机版本——项目介绍 | 输入子系统(按键) | 单元测试

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;项目简介&#x1f3c0;输入子系统(按键)⚽应用层⚽设备层⚽ 内核层抽象层⚽…

flink选择slot

flink选择slot 在这个类里修改 package org.apache.flink.runtime.resourcemanager.slotmanager.SlotManagerImpl; findMatchingSlot(resourceProfile)&#xff1a;找到满足要求的slot&#xff08;负责从哪个taskmanager中获取slot&#xff09;对应上图第8&#xff0c;9&…

IDEA 使用

目录 Git.gitignore 不上传取消idea自动 add file to git撤销commit的内容本地已经有一个开发完成的项目&#xff0c;这个时候想要上传到仓库中 Git .gitignore 不上传 在项目根目录下创建 .gitignore 文件夹&#xff0c;并添加内容&#xff1a; .gitignore取消idea自动 add…

【OpenCV-Torch-dlib-ubuntu】Vm虚拟机linux环境摄像头调用方法与dilb模型探究

前言 随着金秋时节的来临&#xff0c;国庆和中秋的双重喜庆汇聚成一片温暖的节日氛围。在这个美好的时刻&#xff0c;我们有幸共同迎来一次长达8天的假期&#xff0c;为心灵充电&#xff0c;为身体放松&#xff0c;为未来充实自己。今年的国庆不仅仅是家国团聚的时刻&#xff…

STL容器

C STL STL实现原理及其实现 STL&#xff08;Standard Template Library&#xff0c;标准模板库&#xff09;&#xff0c;提供了六大组件&#xff0c;可以相互之间组合套用&#xff0c;这六大组件分别是&#xff1a;容器&#xff08;Containers&#xff09;&#xff0c;算法&a…

1.6.C++项目:仿muduo库实现并发服务器之channel模块的设计

项目完整版在&#xff1a; 文章目录 一、channel模块&#xff1a;事件管理Channel类实现二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、代码&#xff08;一&#xff09;框架&#xff08;二…

凉鞋的 Godot 笔记 103. 检视器 :节点的微观编辑和查看

在上一篇&#xff0c;笔者简单介绍了场景与节点的增删改查&#xff0c;如下所示: 在这一篇&#xff0c;我们接着往下学习。 我们知道在场景窗口&#xff0c;可以对节点进行增删改查。 在 Godot 引擎使用过程中&#xff0c;场景窗口的使用频率是非常高的。 但是场景窗口只能编…

macOS Sonoma 14 正式版(23A344)发布,附黑/白苹果镜像下载地址

系统介绍&#xff08;系统下载地址&#xff1a;http://www.imacosx.cn/115300.html&#xff09; 黑果魏叔9 月 27日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS Sonoma 14 正式版&#xff08;23A344&#xff09;。 macOS 14正式版系统发布&#xff1a;全新功能与改…

ChatGPT Prompting开发实战(十二)

一、如何开发prompts实现个性化的对话方式 通过设置“system”和“user”等roles&#xff0c;可以实现个性化的对话方式&#xff0c;并且可以结合参数“temperature”的设定来差异化LLM的输出内容。在此基础上&#xff0c;通过构建一个餐馆订餐对话机器人来具体演示对话过程。…

Arcgis克里金插值报错:ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。

Arcgis克里金插值报错&#xff1a;ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。 问题描述&#xff1a; 原因&#xff1a; shape文件的问题&#xff0c;此图可以看出&#xff0c;待插值的点有好几个都超出了地理范围之外&#xff0c;这个不知道是坐标系配准的问…

JAVA设计模式-代理模式

一.概念 在软件开发中&#xff0c;也有一种设计模式可以提供与代购网站类似的功能。由于某些原因&#xff0c;客户端不想或不能直接访问一个对象&#xff0c;此时可以通过一个称之为“代理”的第三者来实现间接访问&#xff0c;该方案对应的设计模式被称为代理模式。 ​ 代理模…

数据结构——堆(C语言)

本篇会解决一下几个问题&#xff1a; 1.堆是什么&#xff1f; 2.如何形成一个堆&#xff1f; 3.堆的应用场景 堆是什么&#xff1f; 堆总是一颗完全二叉树堆的某个节点总是不大于或不小于父亲节点 如图&#xff0c;在小堆中&#xff0c;父亲节点总是小于孩子节点的。 如图&a…

GO 比较两个对象是否相同

本文主要是来聊一聊关于 Golang 中的深度比较 DeepEqual 因为最近发现身边的小伙伴写 2 个或者多个 map 比较的时候&#xff0c;都是自己去实现去比较每一个结构&#xff0c;每一个节点的 key 和 value 是不是都相等&#xff0c;且根据不同的数据结构&#xff0c;都要去实现一…

【Python】基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作

【Python】基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作 文章目录 手势识别人脸追踪键盘控制整体代码附录&#xff1a;列表的赋值类型和py打包列表赋值BUG复现代码改进优化总结 py打包 视频&#xff1a; 基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作 手…

Everything+cpolar搭建在线资料库,实现随时随地访问

Everythingcpolar搭建在线资料库&#xff0c;实现随时随地访问 文章目录 Everythingcpolar搭建在线资料库&#xff0c;实现随时随地访问前言1.软件安装完成后&#xff0c;打开Everything2.登录cpolar官网 设置空白数据隧道3.将空白数据隧道与本地Everything软件结合起来总结 前…

Python日期的加减等操作

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 日期输出格式化 所有日期、时间的api都在datetime模块内。 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都…

scala基础入门

一、Scala安装 下载网址&#xff1a;Install | The Scala Programming Language ideal安装 &#xff08;1&#xff09;下载安装Scala plugins &#xff08;2&#xff09;统一JDK环境&#xff0c;统一为8 &#xff08;3&#xff09;加载Scala &#xff08;4&#xff09;创建工…