没有运算符重载怎么办:自定义比较器与 transacts 效果
第 13 课说过,普通蓝图实例不能用 = 直接比较,想给自己做的类型「教会」大于小于就更没门了——Verse 目前不开放「运算符重载」(就是给自定义类型自己定义 +、< 这些符号该怎么算)。这一页从论坛上一条真实的报错出发,看社区怎么绕过这堵墙,顺便认识一个新朋友:transacts 效果标注。
一、论坛现场:一条报错引出的两个问题
Epic 开发者论坛上,有位开发者想给自己的 CustomPlayer 类型写一个比较用的函数,用来给玩家排行榜排序。函数本身逻辑简单,可一按 Compile 就撞墙,报红信息是本课读者迟早会见到的一位「熟人」:
This invocation calls a function that has effects that are not allowed by its context: transacts no_rollback
他顺势追问:Verse 能不能像有些语言那样「重载运算符」,直接给 CustomPlayer 定义 <(让「小于」对两个玩家也有意义)?这个帖子的价值就在这里——它一次踩中了两个坑:一个是语言边界(有没有运算符重载),一个是效果系统(为什么报 transacts no_rollback)。
二、答案一:UEFN Verse 目前没有运算符重载
先回答语言边界问题:不能。你在官方 API 文档里确实能看到 operator'+=' 这样的条目,但那只是内建运算符在官方库里的「户口登记」,不代表你可以给自己做的类型上新户口——UEFN Verse 现阶段不支持为自定义类型定义运算符。
所以,想让自定义类型「可比较」,路只有两条:要么比较它内部本来就能比的字段(整数、字符串等),要么按第 13 课讲过的办法给类标 <unique>,用 = 做「是不是同一个实例」的身份比较。至于大小排序,就得自己写个普通函数来当比较器(一个负责「谁大谁小」的小函数)。
三、答案二:被采纳的重构——比较器只收分数,并标上 transacts
再看效果系统问题。帖子里被采纳的答案没有硬刚运算符重载,而是改了两处:第一,让比较函数别接收整个 CustomPlayer,只收提前取好的那个整数分数——比较逻辑越纯粹越好;第二,给函数显式挂上 <transacts> 这个效果标注:
# 被采纳的做法:比较器只接收分数,并标注 <transacts>
CompareScores<public>(A:int, B:int)<transacts>:int =
if (A < B) then -1 else if (A > B) then 1 else 0
读图翻译:这是一个自定义函数 CompareScores,接收两个整数 A、B,返回一个整数。函数体是一串 Branch 判断:A 比 B 小就回 -1,A 比 B 大就回 1,否则(相等)回 0——正好对应蓝图里拿两个 Integer 比大小、按结果输出 -1 / 1 / 0 的一串节点。名字后面的 <transacts> 是给这个函数贴的一张「我可以被回滚」的标签。
为什么 <transacts> 是关键?回想第 12 课:Branch 这类「能走不通」的判断里,代码是可回滚的——走不通就把做过的改动整体撤销、当没发生过。当你把一个函数当「零件」塞进这类可回滚的场合(比如排序时要在「能走不通」的位置反复调用你的比较器)时,这个零件自己也得承诺「我可以被回滚」——这个承诺就是 <transacts>。不标,编译器就当它默认带着「不可回滚」的效果,于是一按 Compile 就甩出上面那条 transacts no_rollback 报红。
顺带注意函数体:if (A < B) then -1 else if (A > B) then 1 else 0 把好几个 if…then…else 串成一行,整串直接当成函数的输出值——正好复习第 12 课「if 能当值用」和第 13 课「没有三元简写就用 if then else(像蓝图的 Select 节点)」两个知识点。至于把比较器具体传给哪个排序功能,各版本不一样,以 Verse API Reference 为准。
四、随堂小测 ★
把一个比较函数传给一个要求「能被回滚」的场合(比如排序),却报 transacts no_rollback,最可能的原因是?
本文整理自 Epic 开发者论坛的问答帖:How to operator overloading in Verse — Unreal Engine Forums ↗