为 I/O 以外的事物重载移位运算符是否是一个好的设计?
Can overloading shift operators for things other than I/O be a good design?
我正在为 A* 搜索算法实施开放列表 (OL) class。 OL 基本上是搜索节点的专用优先级队列。在描述 A* 算法的伪代码中经常看到这样的符号:
successorNode -> OL // put the successor node into OL
...
curNode <- OL // get the best node from OL and store it in curNode
三个问题:
我的 OL class 通过重载移位运算符来支持类似的符号是否有意义:
OL ol;
...
OL << successorNode;
...
OL >> curNode;
(仅当 1. 的答案为 "Yes" 时)我能否支持这个(即 cout
和 [=16 不支持的用法) =] 对于内置类型):
OL ol;
...
successorNode >> OL;
...
curNode << OL;
(仅当 1. 的答案为 "Yes" 时)移位运算符的这种用法对标准容器是否有意义:
vector<int> v;
v << 5; // instead of v.push_back(5)
编辑:这个问题的目的有两个:
询问所提出的设计是否违反了重载运算符应该模仿这些运算符对built-in/standard类型的含义的原则。
询问为什么不使用移位运算符来减少标准容器的冗长用法。
您问题的答案很可能主要基于个人意见,因为没有硬性规定 allow/disallow 这种运算符重载的用法。因此,我将提出一些论据来帮助您决定这是否是一个好主意,而不是硬性回答。
关于你的前两个问题:
从最小惊奇原则的角度考虑一下。如果有人看到你的代码,他会期待什么?会立即清楚这是什么意思,或者,作为相反的极端,他(她)会期待完全不同的东西吗?如果适用,重载 值得 惊喜吗?比如,了解了operators是干什么的,是不是让代码更清晰?如果利大于弊,那就去吧!否则不要。
作为这一点的侧节点,我什至遇到了这样的论点,即 iostream 运算符是运算符重载的一个坏例子,因为它们不移动整数。但是,我倾向于不同意,并将其视为个人意见。
应用于您当前的情况:用户可能希望通过呼叫接线员获得其他结果吗?例如。他可能会期待队列中的另一个结果吗?如果是这样,请不要超载。还是希望用户熟悉伪代码符号,并看到相似之处?如果是这样,请超载!
关于第三个问题:
有人同意,有人不同意。例如,Qt 框架的容器支持这种用法:
QList<int> list;
list<<5;
总结:
答案取决于它是否使您的代码更具可读性(当然还有个人意见)。
注意:所有这些仅适用于没有风格指南或禁止使用运算符重载的情况!
这是一个风格问题,所以这是我的 2 美分:我喜欢重载这些运算符,这有助于获得简洁明了的语法,并且用法与 <iostream>
一致。例如:
void MyLoggableClass::foo(int i)
{
LOG_TRACE("foo(" << i << ") called");
}
还有另一个宏可以促进这种用法并记录进入和退出,但你明白了。虽然我没有将它用于容器或容器适配器,但我想它对 "stream-like" containers/adapters 很有意义,尤其是 FIFO 队列、优先级队列甚至堆栈。 Qt 的用法有点难以接受,因为你必须猜测新项是添加在列表的前面还是后面。我假设它是背面,文档证实了这一点,所以这并不奇怪。但是,它们没有 operator >>
并且这是有道理的,因为我不知道它会从列表的哪一端弹出该项目。问题 1 是肯定的,问题 3 是 "somewhat"。
关于第二个问题,我强烈建议不要这样做。为什么?虽然对称性看起来不错,但它对 <iostream>
的用户来说更令人惊讶,即使我们忽略这一点,它也会打开一罐蠕虫:
queue<item> q;
item1 >> q << item2;
真的吗?即使我们同意语法是合适的,一个偶然的 reader 现在可能不得不查找这些运算符是左结合还是右结合,只是为了了解哪个项目首先进入队列(对于优先级排队当然会更重要)。但下面的情况更糟。假设您有以下代码:
queue<int> q;
q << 3 << 50;
但后来有人决定他更喜欢预先设置值并将其重写为:
queue<int> q;
50 >> 3 >> q;
Sanity 现在终于离开了我们——这会将单个值 (6) 推入队列,因为它被评估为 (50 >> 3) >> q
(具有整数位移位,结果为值 6)。我认为这也是为什么 <iostream>
.
从未考虑过这种用法,或者可能考虑过但被驳回的一个重要原因。
我正在为 A* 搜索算法实施开放列表 (OL) class。 OL 基本上是搜索节点的专用优先级队列。在描述 A* 算法的伪代码中经常看到这样的符号:
successorNode -> OL // put the successor node into OL
...
curNode <- OL // get the best node from OL and store it in curNode
三个问题:
我的 OL class 通过重载移位运算符来支持类似的符号是否有意义:
OL ol; ... OL << successorNode; ... OL >> curNode;
(仅当 1. 的答案为 "Yes" 时)我能否支持这个(即
cout
和 [=16 不支持的用法) =] 对于内置类型):OL ol; ... successorNode >> OL; ... curNode << OL;
(仅当 1. 的答案为 "Yes" 时)移位运算符的这种用法对标准容器是否有意义:
vector<int> v; v << 5; // instead of v.push_back(5)
编辑:这个问题的目的有两个:
询问所提出的设计是否违反了重载运算符应该模仿这些运算符对built-in/standard类型的含义的原则。
询问为什么不使用移位运算符来减少标准容器的冗长用法。
您问题的答案很可能主要基于个人意见,因为没有硬性规定 allow/disallow 这种运算符重载的用法。因此,我将提出一些论据来帮助您决定这是否是一个好主意,而不是硬性回答。
关于你的前两个问题:
从最小惊奇原则的角度考虑一下。如果有人看到你的代码,他会期待什么?会立即清楚这是什么意思,或者,作为相反的极端,他(她)会期待完全不同的东西吗?如果适用,重载 值得 惊喜吗?比如,了解了operators是干什么的,是不是让代码更清晰?如果利大于弊,那就去吧!否则不要。
作为这一点的侧节点,我什至遇到了这样的论点,即 iostream 运算符是运算符重载的一个坏例子,因为它们不移动整数。但是,我倾向于不同意,并将其视为个人意见。
应用于您当前的情况:用户可能希望通过呼叫接线员获得其他结果吗?例如。他可能会期待队列中的另一个结果吗?如果是这样,请不要超载。还是希望用户熟悉伪代码符号,并看到相似之处?如果是这样,请超载!
关于第三个问题:
有人同意,有人不同意。例如,Qt 框架的容器支持这种用法:
QList<int> list;
list<<5;
总结:
答案取决于它是否使您的代码更具可读性(当然还有个人意见)。
注意:所有这些仅适用于没有风格指南或禁止使用运算符重载的情况!
这是一个风格问题,所以这是我的 2 美分:我喜欢重载这些运算符,这有助于获得简洁明了的语法,并且用法与 <iostream>
一致。例如:
void MyLoggableClass::foo(int i)
{
LOG_TRACE("foo(" << i << ") called");
}
还有另一个宏可以促进这种用法并记录进入和退出,但你明白了。虽然我没有将它用于容器或容器适配器,但我想它对 "stream-like" containers/adapters 很有意义,尤其是 FIFO 队列、优先级队列甚至堆栈。 Qt 的用法有点难以接受,因为你必须猜测新项是添加在列表的前面还是后面。我假设它是背面,文档证实了这一点,所以这并不奇怪。但是,它们没有 operator >>
并且这是有道理的,因为我不知道它会从列表的哪一端弹出该项目。问题 1 是肯定的,问题 3 是 "somewhat"。
关于第二个问题,我强烈建议不要这样做。为什么?虽然对称性看起来不错,但它对 <iostream>
的用户来说更令人惊讶,即使我们忽略这一点,它也会打开一罐蠕虫:
queue<item> q;
item1 >> q << item2;
真的吗?即使我们同意语法是合适的,一个偶然的 reader 现在可能不得不查找这些运算符是左结合还是右结合,只是为了了解哪个项目首先进入队列(对于优先级排队当然会更重要)。但下面的情况更糟。假设您有以下代码:
queue<int> q;
q << 3 << 50;
但后来有人决定他更喜欢预先设置值并将其重写为:
queue<int> q;
50 >> 3 >> q;
Sanity 现在终于离开了我们——这会将单个值 (6) 推入队列,因为它被评估为 (50 >> 3) >> q
(具有整数位移位,结果为值 6)。我认为这也是为什么 <iostream>
.