V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  FrankHB  ›  全部回复第 27 页 / 共 92 页
回复总数  1830
1 ... 23  24  25  26  27  28  29  30  31  32 ... 92  
我的习惯是要,理由是缩略词用完整形式替换,该大小写的地方都不会变,有一致性。
不过这可能会导致和其它前缀混起来不容易看清,比如.NET 的 I 接口前缀……于是微软之类似乎都不鸟这个。
2022-02-27 21:07:30 +08:00
回复了 helone 创建的主题 程序员 那个 Github 被封的,自己真不知道为什么吗?
@xfriday 违规被封是活该,但违规的是人而不是任意内容,删的也不是违反规则的内容,影响到非特定公众那就是开始垄断恶心人的味道了。

这事跟 GitLab 丢代码可以看成同类风险,都是服务可用性不靠谱,免费用户没人权不会被补偿。而实际影响只会更恶劣,因为一来代码只要不傻到没另外备份一行 git 命令都能整回来,而 issue 之类的东西未必价值比代码低,真要掉了,就没那么容易恢复了;二来 GitHub 显然表现得拒绝认为这是问题而不会补救;三来 GitHub 更流行看不到风险的用户更多。

我不关心一个两个倒霉或者活该的用户,除非是我自己有关的公众利益问题。这里违规活该或者可耻的是非很明显,但对我影响更大的是 GitHub 乱删跟违规没有因果关系的内容显然做过头了(前提是属实,但这个不像假的)。跟论坛氛围一样,一般一个用户不太会认为自己的产出有价值而当作垃圾堆一样无关紧要的地方是不太适合投入精力承载工作内容,这种全局问题的对我而言比任何一个两个薅羊毛的用户都恶心得多。虽然我跟 Linus Torvalds 一样认为 GitHub 仅仅就是 fine for hosting ( https://github.com/torvalds/linux/pull/17#issuecomment-5654674 )但别的 UI 做得不怎么样( disclainer:我强烈反对他之后关于 word rwap 的观点)而刻意不怎么依赖 GitHub (只要是我能决定的部分,所有正经工作都只是把 GitHub 当作备胎),我仍然不会乐意分发我的工作内容的平台越来越像对 git 以外的内容都完全不提供担保的垃圾堆。遗憾的是我无法改变大多数我贡献的项目依赖 GitHub 的决策,所以我只能谴责 GitHub 作为服务商的道义上的不负责了。
2022-02-27 20:44:36 +08:00
回复了 helone 创建的主题 程序员 那个 Github 被封的,自己真不知道为什么吗?
@yzbythesea 它自然有权那么做,但被因为这种机制的低可用性屑体验恶心到的核心用户嫌弃,也理所当然了。
关键是这种干扰任意无关群众的设计和开源社区预期的讨论氛围格格不入,这显然就是产品质量问题。
对大多数一般用户来讲真出了问题或许影响不大,反正不是 SNS/IM ,没那么大用户粘性。然而要命的是一大坨有全局影响的重要项目(比如最近的 LLVM )都在往这么个不靠谱的平台上迁移,而且投入越大的用户越吃亏,默许这种行为对专业用户的长期影响没法估量。
迫于……
迫真……
2022-02-26 11:28:38 +08:00
回复了 helone 创建的主题 程序员 那个 Github 被封的,自己真不知道为什么吗?
@yzbythesea 它删不删这些代码其实无所谓(反正已经用了 git 了换个地方随便 push ),但是“顺便”连其它活动一起毁尸灭迹就恶心了。
就好比两人在论坛上激情对线了几千楼,结果被其中一方被婊的突然全部看不到了,婊人的如果不每次引用不就婊空气?对当事人和围观群众的体验都是屎。
同样是不打招呼干掉,贴吧都没那么垃圾。
2022-02-26 11:12:37 +08:00
回复了 helone 创建的主题 程序员 那个 Github 被封的,自己真不知道为什么吗?
@mascteen 好吧,你说 GitHub 那套……或者说 git 那套。那我只能说,如果讨论的是项目,而不是仓库,这个着眼点不对。
对项目来说 contributor 是指 contribute 了什么东西的人。对提供的是内容的,惯例是除了直接具有写权限的项目开发者外,指能放到 contrib/ 的提供者,或者维护者可以用单独一个文件 CONTRIBUTORS 之类列出而不会引起什么争议的涉众。
一些 git 用户可能是为了偷懒,把统计 committer 直接叫 contributor 了。不知道这个风气是 git 还是还是 GitHub 带起来的( git book 里直接拿 GitHub 的工作流当 contributing 的介绍,但是其它非 GitHub 的一些地方也有类似的混淆)。反正我见到的其它 DVCS 用户中不太有这个现象(至少没那么严重)。
归咎于 GitHub 的另一个理由是它的文档直接对项目和仓库的 contributor 进行了混淆: https://docs.github.com/en/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors ,尽管正文说了只是 contribute to commits 。(这里这个 project 用得稀里糊涂,特别是 GitHub 还有个另外含义的叫 project 的特性……)
显然实际上也有对此不满,想要分清楚的 GitHub 用户,比如: https://github.com/all-contributors/all-contributors
2022-02-25 18:32:30 +08:00
回复了 helone 创建的主题 程序员 那个 Github 被封的,自己真不知道为什么吗?
@mascteen “contributors 那一栏”是什么玩意儿。字面上提过正经 issue 被接受的都能算 contributors ,甚至打钱的都能算,社区里自然谁的算谁的。还是你傻傻分不清 contributors/collabrators/authors ?
2022-02-23 11:58:33 +08:00
回复了 lanlanye 创建的主题 程序员 关于软件设计的一些问题
当然这里大部分问题不是 PL 问题能解决的,而来自工程需求的本质困难。光是需求理解的复杂性( NPL:喵?)指望自动解决就差不多是在指望发明强 AI 。
但还是有点相关的:建议参考 Actor model 和 Scheme 的早期历史。
铜币不够先不展开了。
2022-02-23 11:38:11 +08:00
回复了 lanlanye 创建的主题 程序员 关于软件设计的一些问题
淦,上面的 at 怎么掉了……26L Re: @3dwelcome .
2022-02-23 11:36:00 +08:00
回复了 lanlanye 创建的主题 程序员 关于软件设计的一些问题
因为这是显然的倒退。

函数(function) 的另一个同义词是“功能”,本来就有更高层次的抽象的意象在里面。所以编程语言发展过程中会把过程(procedure) 自觉合并到“函数”里,而不管数学上原本的定义是怎么样的。

(另一个原因是数学上的定义本来就不怎么样,混乱程度参考 en.wikipedia.org/wiki/History_of_the_function_concept 词条——看,都需要单独整个条目出来了。)

所以你所谓去函数,会被一般用户理解为去除功能上的模块化抽象。

另一方面,对有点兴趣关心不同语言设计的用户,你说的函数具体是什么,都是个问题。但是既然你用“命令”代替“函数”,那我就当作最常见高级语言里允许带变量名构成参数的那种。

而这样你就走进死胡同里了:你在对抗最常规的高级语言普遍需求——抽象。

学术一点讲,虽然我数落数学定义,但是现代意义上的真正算跟高级语言捆绑在一起的“函数”,其构造器的学名(来自λ演算)就叫“λ抽象”。这货的重要性在于,它是所有日常通用编程语言中叫“函数定义”的东西在操作语义描述中的原型,不管语言的设计者是不是意识到了这点。

为什么命名为抽象?因为它提供了把变量绑定作为形式参数的现代意义上的“函数”的普遍功能,而变量名允许用户自行随意决定而原则上不改变程序的语义稳定性(α-转换),或者说,允许定义函数的作者以决定变量名的方式夹带私货,隐藏实现。配合抽象的应用(函数调用)的结果能被作为抽象的操作数(函数参数),以此能组合出最基本的接口和实现分离提供微观上的可维护性——这是实用意义上最基本的可编程性的一部分。

因此即便原版λ抽象也不支持副作用(以之后几十年的时髦话来说,纯函数式),拿来给人编程,在很大程度上就足够比任何不提供抽象的语言好用。

相比之下,大多数传统数学家(不算整数理逻辑之类的)习惯的那种所谓的函数更适合叫“映射”,严格定义(例如通过笛卡儿积或者数学关系)上只是一种更一般意义上的函数的没多少可编程性的实现方式罢了。

完全没有限制的抽象可能会被滥用,所以也被一些用户批判,比如 FORTRAN 和 BNF 之父 J. Backus 。他在 1977 年图灵奖演说中发牢骚并提出要以所谓的 FLP(function level programming) 解决当前语言的哲学问题。这个方案的核心就是消灭抽象——带变量的函数,让所有函数回归到没有变量而通过组合的更接近传统数学上容易排除私货而更容易推理的玩意儿,所谓 point-free style ( point 就是参数名)。

但这显然没有工程意义上成功到哪去。甚至学术上也不咋地,因为计算模型上撑死就只是μ-递归函数这套,实在没多少新东西。这番推广倒是便宜了一字之差的 FP(function programming) ——而后者是显然接受甚至鼓励λ抽象的。

FLP 失败的根本原因是,禁止用户使用函数参数就是“不好用”。(考虑基本没什么人用,具体现代例子还真不太好举例,比如想想只准管道不准 xargs 之类的 shell ……)要克服 FLP 风格的抽象困难特别是不容易表达常规递归算法描述的解的问题,用户程序基本得像早期 Lisp 语言一样要发明动态的环境(实际通常不具备像λ演算一样用替换实现“调用”的条件,至少非纯函数不大行)甚至词法闭包去重新实现抽象;考虑到实现λ抽象是λ演算的语义上的工作量的 50%以上(这保守了,实际一般起码 80%,如果考虑类型系统等其它特性经常对函数定义有逻辑依赖的话),这四舍五入基本上就是自己实现一个新语言的解释器了。而 FLP 语言又不是汇编这样能容易让硬件支持来换取存在感的语言,在这里还不如直接从实现栈里一脚踹开,站茅坑不拉屎在实用上是完全不合格的。

再者,强调 point-free 也不是什么新的东西,组合子逻辑在 1950 年代早就整出来了——但也就是更早(不晚于 1940 年代)的λ演算上抠掉自由变量的缩水版整理出的(虽然组合子本身的历史可以追溯到 1920 年代)。而使用体验呢?除了纯函数式那套,比汇编还汇编(连寄存器都没)。甚至实际可用的组合子逻辑实现如 unlambda ,为了写出和“传统”语言功能等价的代码的一般方法,还得指望用户自己先构造λ抽象然后人肉编译去掉其中的具名参数。另一种路线是 APL——函数命名炸了(自行查找代码怎么写的)。

这样的语言,能被多数高级语言用户接受才有鬼了。

这里的缩水版函数(组合子),与其说是大道至简的 basic building block ,倒不如说只是一种(通过抽象这种构造器引入的)健全版函数的丐版,甚至都未必有传统数学上的映射实用(映射的描述是起码可以选配抽象的,只是形式上很不严格);毕竟计算模型上考虑语义而不仅仅是表面的语法,和组合子同等重要甚至更根本的东西多了去了。

而所谓的“命令”,不说是不是被抽象养刁了的编程语言用户接受(大部分还是非纯函数式语言的抽象,组合子都没法这样扩展),连学术上引起的兴趣都远不如组合子。

考虑历史,“命令”本质上是什么呢?其实就是因为一开始实现困难,而不那么强调组合的低级语言中间产物——命令或者说指令,早期基本就是机器操作助记符的同义词。之后即便在 FORTRAN 之类的高级语言中推广,但是增加的功能也相当抠,说白了是另一种风格的丐版函数。和组合子的丐版不同在于,它没 point-free 那么极端地拒绝使用不能作为函数组合的参数,但也没一般编程语言强调允许递归地把结果作为实际参数。结果就算不像组合子在高级语言可编程性上那么反人类(还得人肉编译才能写出多数编码),就是鸡肋中的战斗机。

至于为什么讲到 DSL 看上去更容易接受“去函数”?因为 DSL 的目标用户通常没有经过基础的编程语言训练——不但没编程语言理论(大多数程序员其实也都没有),而且缺乏任何一种支持不丐版函数的通用编程语言的学习,所以理解不了“函数”的现代内涵,会把“函数”的理解自觉停留在中学阶段那种漏洞百出的玩意儿上,更加想象不出“函数”能用到怎么灵活的程度。这样,“命令”这种看上去无关的强大的“实用”货色,才会容易被接受。

(其实内部鄙视链也是有的,比如因为解决不了 funarg 问题等实现原因而无法支持一等函数的类 ALGOL 语言和拐弯用“方法”支持自由函数的所谓面向对象语言的用户,就经常在所谓“函数式”语言的用户的火力范围之内。)

对任何有点通用目的编程语言使用经验的用户来说,对不那么丐版的函数(包括“过程”)有点体验就会食髓知味,自然回不去被强迫使用“命令”了。(这也是为什么 shell 不受很多程序员待见的原因之一;虽然讲道理 shell 的函数没那么丐,还有个原因是不容易完全写对,维护起来太作妖了。)

所以你的“微观上是函数,宏观上就自然升级到了指令”根本是外行人^n (n>=2) 的无稽之谈。

(就不说 CPU 指令到底是不是微观的问题了,反正大部分用户都不会直接碰这个去编程。)
2022-02-01 15:49:17 +08:00
回复了 amiwrong123 创建的主题 C++ 项目中这样去隐藏类的真正实现 是种好的做法吗?
看这代码,不好。虽然思路不见得错。

首先,ModuleBase(ModuleBase*) 的参数含义就不明确,没文档还得看实现:谁有所有权?
即使.h 里的 private 成员类型能让用户看得到,但正常情况下就不应该看这个(需要看的 private 成员仅限虚函数)。

其次,“开启定时器”这么具体的功能可以“通用”到和 module 有什么关系?
没套个命名空间限定,这 module 的说法就可疑到要猜到底有没清楚从哪些具体逻辑中抽象出什么。
如果这类具体功能必须在公开 API 中体现,那么要么 ModuleBase 这命名不够清晰,要么这更适合在类外以自由函数的方式提供(所谓“扩展方法”)。
虽然你可以辩解说这样纯粹只是作为虚指的例子,不具有实际含义;但这里要强调,是否放在类的继承体系中,和具体功能通常强相关。
总体原则是便利接口放类外而类只提供必要接口(避免 std::basic_string 膨胀到阻碍易用和扩展性的教训),但什么功能算“必要”的判断标准依赖具体业务逻辑。

第三,不是说这样实现不好,而是用户的这种心智包袱有问题。
跟 OO 意义两回事,C++对象不一定是类的实例,建模上可能没意义;运行时开销也是原则上可以预测的(反正你没折腾虚基类),除非真有证据发现或者高度怀疑构成性能瓶颈,实现不应患得患失。
在多出来的对象仅用于实现时,就应该是“使用者感觉不到”的。(反过来因为感觉不到过头了,才会纠结 std::launder 这种。)
而作为设计者,你自己都搞不定自己的心智包袱,那么设计的确可能是不稳定的(可能改变主意返工,白写,给用户添乱)。
通过指针的 pImpl 是个标准做法(另一个不需要指针的做法是 @Pezy 提到的 Priv )。除具体逻辑外,是否合适的确存在一些实现上的计较,但你都没感觉到,那么大可忽略。
这些做法最明显的实现问题是对被隐藏对象类型的完整性要求,所以直接= default 的特殊成员函数就没法直接用。(当然,类的用户无需关心。)光是因为这个原因我就会考虑减少这些隐藏的使用,直到考虑清楚这确实是逻辑上有必要隐藏的边界。
更要紧的:过早引入兼容性保证会让以后发现不适合维护这种兼容性时更加蛋疼。如果代码被分发再返工,用户也会感觉到疼。这时候就不只是心智包袱的问题了。

第四,需要虚函数不是非得避免组合的借口。
你这里的 pImpl 还是挺简单的,才一层而已……
要我写:

class ModuleBase /* final ,放这里一般不太好,可能有非 virtual 用途的继承比如当 mixin ;想要禁止的一般只是 public 继承*/
{
private:
function<void(int)> onTimer_Impl; // 反正就当例子,这看不出用途的签名我就不多吐槽了……
public:
//激进一点,onTimer 也可以暴露成 public ,如果你敢承(甩)担(锅) bad_function_call 之类的风险。
//...
};

避免虚函数的原因是大多数设计者根本没有考虑到 virtual 能够做什么他们能预料的破事。
具体地,dtor 外的虚函数的心智包袱远远比一般函数以及可调用对象更大,除非是实现中完全能自己控制 overrider ,否则用户能用各种瞎继承(对 public ,还有 C::f()和 x.f()的调用姿势;对非 void 返回类型,还有协变)混起来让你破防。
特别注意,private 的也可被重写。

另外,“需要重写”和“能重写”是两回事。如果要求重写,那可能更适合用虚函数,但同时应该用= 0;做成纯虚函数。
好处也就是这种= 0 强制静态检查,不需要多用文档约定用户接锅 bad_function_call 之类的而已。
注意纯虚函数仍可有定义提供默认实现。
(要求纯虚函数是 Java 等习惯上要求 interface 的同义词,即便是这样作死花样不那么多的语言也有一些类似的坑;但 C++中用 virtual 整个就是个可选项,不那么死板,所以不需要那么强调。)

第六,startTimer 不是虚函数的决策就通常逻辑讲是对的(但是,是不是非得访问 impl 而不是 module 的其它公开 API 实现仍然存疑;另见以上第二点)。
然而 NULL 判断说明你很可能做错了,因为典型情况下 pImpl 的 p 仅仅是要间接使用而不蕴含 nullable ,非空是个不变量(跟上面的 function 实现正常不可能 bad_function_call 一样)。
例外是你这个 Module 是真能 move 出空状态的不寻常设计。但你都要用 virtual 了,逻辑上更没法随便要求派生的实现能 move 。

第七,@zwzmzd 提到的是特定于 pImpl 的一个应用——应对 ABI 兼容问题,但实际上经常不那么顶用,也确实不常用;但这不仅是 C++实现的问题,还有更多整体原因。
一般来讲,UNIXy 下更多用 soname 而不是替换掉 .so 。(虽然我不喜欢这些选项;我更乐意多版本共存部署,但是包管理都比较坑就是了,除了 nix 等少数方案外都得手动,自动也不省心。)
而且都 android::sp 了,这问题就更没显著性了。Android 尤其不注重跨 apk 共享 .so (甚至某些著名 app 还没事多复制包括.so 的文件备份),也不需要你太关心升级时的问题,大不了整个替换掉。

最后,是不是真值得来个 Impl/Priv 隐藏,这就跟具体业务逻辑全面相关了。
不知道 Module 具体应该干什么,变更需求有多频繁时,没法知道收益是否够大。提供个别具体函数的实现也看不出个大概。
2022-02-01 14:21:31 +08:00
回复了 liuser666 创建的主题 Windows 又看了一天的 Windows UI 相关的文档...
@shayuvpn0001 WinForms 永远干不翻最大的设计上的坑:依赖 HWND 。
所以一旦遇到控件这个层次上不能解决的问题(比如说,自己实现不能通过组合现有控件完全实现的新控件),问题就很可能陡然恶心起来:很可能用户就得把 Win32 UI 的花里胡哨的屎味咖喱过一遍,还比原生 Win32 破事更多(因为涉及互操作)。
Win32 的屎味咖喱也是为什么传统的搞 Win32 UI 的受不了纷纷跳出来搞新的一套所谓的 DirectUI 。
讲道理,原生的 HWND 其实确实是希望用户按传统意义的方式扩展的。这特别体现在任何一个像样的 Win32 控件都是桌面隐喻的所谓“窗口”( Windows 这个名字也指的是这个)上,而不是现在大多数最终用户理解的窗体和对话框。
但是 Win32 API 用 C 提供,不管对用户使用还是维护者扩展都有天坑(用户看不到 HWND 实现,微软也没可能让用户彻底看到),加上一些底层设计问题(如滥用低效的异步窗口消息、WndProc 的签名扩展性差甚至要用户折腾 thunk )和 Windows API 固有的更新周期导致跨操作系统版本体验碎片化(比如分层子窗口只在 Windows 8 后支持)导致这种方式最终是无药可救的。
在扩展 HWND 无望的情况下,Windowless 是自然选项了。如果把扩展 HWND 实现这样的权利看作是用户(桌面系统开发者)的自由,那么所有健全的 GUI 天生都是所谓的 DirectUI (所以这词虽然是微软开发者发明的,但也挺 low 的),而基于 HWND 封装的方案(包括 MFC 和 WinForms )只是残废版而已。
WinForms 这种咖喱味的屎在常规 Win32 用户的面前解决了旧的 Win32 C API 的一些屎味和随着操作系统版本分发导致的部分表面问题,但没办法解决根本问题(而且 .NET Framework 的分发周期早年一样挺欠揍的)。对原有 Win32 的就是只想搓出来个能看的 GUI 的最传统的用户,API 和工具易用性提升的开发效率已经足够可观,所以才能忍受;但对原本就受不了 HWND 又没法找到现成方案的用户再说,最终需要 hack Win32 的问题也是忍无可忍的。(所以现在还流行的就包括大部分倒腾工控机的,因为这些用户大多对干掉后者的屎味感知不大。)
(题外话,任意不是靠模拟来实现的“原生”GUI 风格的解决方案,不管是什么平台的,一样很可能有这里的残废屎味;至少遇到 Windows 就很可能放纵了;其实还有少数比 Win32 还烂的,比如各种 CAD 之类的私有界面扩展 API ……不过不提也罢。)
WPF 甩掉了 HWND 的包袱,才算是一个相对“完整”的 GUI 解决方案;而且相对 Win32 过渡到 WinForms ,工具的改进使过渡的体验更加平滑,这才是 WPF 的基本盘。至于 MVVM 和 XAML ,其实不像一些用户理解的那么适合传统桌面开发,既是加分项又是减分项(不过最传统的一些用户有些就只是待在 WinForms 觉得够用挺好,也就不会有什么有效反馈了)。当然,光说完成度,WPF 就够打翻 UWP 和现在的 WinUI 的了。
2022-02-01 13:53:18 +08:00
回复了 liuser666 创建的主题 Windows 又看了一天的 Windows UI 相关的文档...
Electron 唯一的缺点显然不只是太大了。一些其它问题最终用户也能感知到。
区别无非是用户够多,坑能相互活埋,不太会有拎不清楚常规开发需求的维护者主动跳出来暴露智商,例如: https://github.com/dart-lang/language/issues/490
事实上,WPF 一样有类似的问题,但实现的优化质量好得多,以至于不少开发者自动忽略了。但是其它多数实现不大有这个余裕。
2022-01-31 20:35:00 +08:00
回复了 wuruxu 创建的主题 生活 不喜欢开车,这个怎么破?
@Cu635 想起这么回事已经是若干年以后了。

还不如当作没有了。
2022-01-30 05:16:23 +08:00
回复了 amiwrong123 创建的主题 C++ 万能引用进行重载的替代方案:标签派发的疑问?
@dangyuluo “相信编译器”是说给那些怀疑自己比编译器聪明但实际上没有什么基础(甚至连生成的代码都不知道怎么看)的用户听的,不适用于 OP 。
对 OP 的情况,该强调的是“不要做过早的优化”,但是判断什么算过早,根本还是在用户自己。

对性能要求极端一些的情况下,其实主流编译器真不那么靠谱;甚至可以反过来说如果要求确实很高时,一些关键的上下文怎么都不该相信编译器(事实上,这种必要场合并不多,开发者应避免任意诉诸审查编译器生成代码为导向进行优化的倾向)。
举个例子,如果嵌套层次多的话(取决于调用上下文和具体编译器版本乃至 cc1plus 之类的参数),启发式内联是可能会睁眼瞎的,于是明明这种简单的情况真不给你内联……
再举个例子,至少 GCC (-O3 -flto )到现在都不大能现实地保证 const allocator_type& 参数能优化得跟 allocator_type 传值质量一样高,也许这就是 C++ 没 restrict 就得老实躺平的宿命了(最蛋疼的是标准库都是钦定 const allocator_type& 的,要风格一致嘛)……
GCC __attribute__((__always_inline__)) 可能生成错误的代码;
作为常识,内联不是越多越好,__attribute__((__flatten__)) 可能是神器也可能是狗屎(另外 GCC 和 Clang 实现不大一样,后者是嵌套 always_inline 偷懒了),有时候反而 __attribute__((__noline__)) 才是救星……

当然以上属实比较极端了,应用开发者一般当作过眼云烟,折腾 framework 也最好别上头。
2022-01-30 04:56:58 +08:00
回复了 amiwrong123 创建的主题 C++ 万能引用进行重载的替代方案:标签派发的疑问?
@GeruzoniAnsasu 另外我还是得提一下,那些 std::true_type 之类的重载 C++17 以来确实可以用 if constexpr 替代,但 OP 那个借楼问 enable_if 例子还就不行(甚至是个 C++20 concept 也不好用的地方),因为是哪来防止隐式上下文里非预期的 overload 的,而不是你自己能手动决定什么时候能加 if constpexr 的地方。如果不踢掉这个 overload ,该传值的转移或者复制构造的调用会匹配到构造模板上。

这是个著名的烂坑,因为太常见(基本上只要任何写单参数构造模板的地方都会坑)我是简写了:
https://github.com/FrankHB/YSLib/blob/master/YBase/include/ystdex/meta.hpp#L948
(实现本身也算是个 enable_if 的应用举例。)
2022-01-30 04:43:33 +08:00
回复了 amiwrong123 创建的主题 C++ 万能引用进行重载的替代方案:标签派发的疑问?
<typename T, typename = xxx> 单独没什么实际意思,就 typename 后的模板形式参数允许省略没用到的名称这个知识点。
大部分情况下,看到不懂的构造,前后的标识符都可以当作关键字。这里如果要搜,直接搜 enable_if 就可以(另外有 C++14 简写 enable_if_t );事实上 enable_if/enable_if_t 原则上就只有一种用法,就是通过 SFINAE 选择需要的 overload/specialization 。

这样也更容易给 enable_if 出现在其它地方的各种奇葩用法打预防针(随便抄几坨我实际写过的):

template<class _tRange, yimpl(typename... _tParams,
typename = enable_if_t<sizeof...(_tParams) == 0>)>
auto
begin(_tRange& c, yimpl(_tParams&&...)) -> decltype(c.begin())
{
return c.begin();
}

template<typename _tFrom, typename _tTo, typename _type = void>
using enable_if_convertible_t = enable_if_t<is_convertible<_tFrom, _tTo>::value, _type>;

template<typename _fCallable, typename _type, typename... _tParams>
auto
invoke_impl(_fCallable&& f, _type&& obj, _tParams&&... args)
-> enable_if_t<is_callable_case1<decay_t<_fCallable>, _type>::value,
decltype((yforward(obj).*f)(yforward(args)...))>
{
return yconstraint(f), (yforward(obj).*f)(yforward(args)...);
}

template<typename _type, enable_if_t<!is_floating_point<_type>::value, int> = 0>
inline bool
Do(const _type& x)
{
return x % _type(2) != _type(0);
}

template<typename _type>
struct is_string_like<_type, enable_if_t<
is_object<decay_t<decltype(std::declval<_type>()[0])>>::value>> : true_
{};

以上只是展示 enable_if 能出现在什么地方,省略了一些为什么需要这么写的上下文(比如其它 overload 和 primary template ),所有共通用法都是 SFINAE (虽然混了点 expression SFINAE )。当然高玩可能可以在缺失上下文的情况下大致上猜出为什么需要(不得不)这样写,并累积对 C++ 的仇恨值而加快升级。
2022-01-30 04:21:54 +08:00
回复了 amiwrong123 创建的主题 C++ 万能引用进行重载的替代方案:标签派发的疑问?
@GeruzoniAnsasu 这个隐含约定在这里不适用。这是给接口的用户看的,而这里实现和实现之间的关系是用户不应该可见的实现细节,所以其实无所谓。
甚至一般有经验的实现者会推荐 OP 的写法。因为这种写法最小化了内部实现和公共接口的依赖,维护者更容易划分出哪些(连续的)代码完全是实现细节,在一定程度上提高了实现内部的模块化,增加了可修改性。

内部实现故意去调用公开接口增加调用层次是不寻常的,特别是行为可能有差异。如:

class B
{
public: void f(){f_impl();}
private: virtual void f_impl(){/*...*/};
public:
void g1(){f();}
void g2(){f_impl();}
void g3(){B::f_impl();}
};

像 f_impl 就算是 private 也允许 override ,所以不是绝对意义上的内部实现。
而这里 g1 g2 g3 的含义就是不一样的。什么时候用什么得看你接口设计是拿来干什么的,而不是有一个教条。
在 B 的实现内部,如果硬要说考虑 f 或者 f_impl 应该怎么调用,那么尽量用 B::f_impl ,因为行为最确定;其次如果有要求允许 B 外的 overrider 就用 f_impl ,至少在 B 这个类内仍同属不可被类外访问的实现细节;最次才是用 f ,表示不同寻常的“就需要依赖外部接口”(此时通常还需要实现的注释)。
B 外要调用,当然是直接 f 了。如果需要在 B 外部 B::f_impl 或者 f_impl ,那这个就不该 private (名字也不该叫 _impl ),不过一般相当罕见。

你所谓的“重新调用 dispatch 的接口”还是“直接调用「你已经知道的那种情况的」具体流程”也应该看设计明确要支持的需求。如果设计不明就会出岔子。甚至极端点说,re-dispatching 这种形式本身就可能是可疑的伪需求。GCC 和 Clang 对 ELF symbol interposition 默认处理的不同就是这类岔子的一个现实例子,这时候就要扯皮了: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100937
2022-01-30 03:21:41 +08:00
回复了 wuruxu 创建的主题 生活 不喜欢开车,这个怎么破?
@retrocode 没摸车不要紧,摸摸驾照。

放置 play 然后过期了……
2022-01-30 02:57:27 +08:00
回复了 phpfpm 创建的主题 Windows 所以 window tray 的这个 bug,从 win95 一直延续到 win11?
进来就盲猜这个问题,果然……

就算不是 feature ,怕是也周知到能当作 feature 了。
1 ... 23  24  25  26  27  28  29  30  31  32 ... 92  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5758 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 32ms · UTC 02:13 · PVG 10:13 · LAX 19:13 · JFK 22:13
Developed with CodeLauncher
♥ Do have faith in what you're doing.