C++ 左箭头运算符(2016)
有趣且“邪恶”的 C++ 惯用法
- 线程把一个自定义左箭头运算符看作既有趣、巧妙,又有点可怕。
- 还提到了其他“邪恶”模式:调用
delete this、先手动调用析构函数再对this做 placement-new,以及侵入式引用计数。 - 有人认为
delete this对侵入式引用计数对象或类似 COM 的Release是可以接受的;也有人更喜欢用智能指针把这类模式隐藏起来。 - 具备仿射/移动类型语言(例如 Rust)被提及为使“销毁 this”在概念上比手动内存
delete更安全。
运算符重载:力量与滥用
- 许多人认为运算符重载只要用得得体就没问题;滥用只是例外,不是常态。
- 有争议或带有玩味的用法示例:
- 用
<</>>表示流、网络消息和容器插入。 - 用
+组合 GUI 小部件。 - 用
*表示 RAII 守卫。 - 用
/表示文件系统路径。 - 诸如“analog literals”和类似箭头或蝌蚪的运算符等奇特构造。
- 用
- 有人称赞某些库(例如线性代数库),认为运算符能大幅提升可读性。
- 也有人认为运算符总体上是设计失误:语义可能性太多(溢出行为等),相比命名函数可读性更差。
Iostreams 与格式化
- 用
<<做 I/O 被批评为糟糕的设计,而且没有传播到标准库之外。 - 一些人强烈偏好格式字符串 API(
std::format/std::print、printf风格,或fmt库),因为更易用且类型安全。 - 反方观点:iostream 的问题大多与运算符重载本身无关。
函数对象、Lambda 与 operator()
- 重载
operator()历史上使函数对象/闭包成为可能;如今 lambda 已覆盖大多数用例。 - 不过,自定义函数对象类型在某些模式中仍被认为是必要的(例如
std::function的内部、多重重载访问器、带行为的析构函数)。
成员指针与委托
- C++ 的成员指针被描述为令人困惑且由实现定义。
- 一种观点最初(后来有所软化)把它们等同于“实例指针 + 函数”的一对;另一些人坚持它们不包含实例状态,只编码偏移和分发数据。
- 与其他语言中的“delegate”或“fat pointer”机制(D、C#、Virgil、Delphi、Python)相比:
- fat pointer 因简单和一等闭包而受到赞赏。
- C++ 更轻量的模型则被辩护为更灵活、更便宜,符合“不为你不用的东西付费”,必要时再用
std::function这种更重的抽象。
- 围绕性能权衡、虚调用、内联和编译模型引发了争论。
关于 C++ 与运算符的总体感想
- 一些评论者表达了对 C++ 的力量与创造性的喜爱;另一些则称其过于复杂,甚至是“滑稽”或“可憎”的。
- 反复出现的张力在于:巧妙的表达力(自定义运算符、各种技巧)与长期代码可读性、可维护性之间的冲突。