需要某种类型的操作符.. c++

4

我想在队列中存储一系列字符串。如果使用成员函数push(),这似乎非常简单。

queue test;
string s0("s0"), s1("s1");

test.push(s0);
test.push(s1);

我正在考虑以隐式方式向队列中添加字符串。这意味着,如果我输入以下字符串序列,例如operator >>应该将字符串值推入队列。

queue test;
string s0("s0"), s1("s1");

s0 >> s1 >> s2 >> s3 ;

有办法实现这个吗?

3
我已经帮你修复了代码格式。这不是通过使用 [CODE] 标签完成的,而是通过将每行缩进四个空格来实现的(在文本框上方点击代码按钮即可实现)。 - Björn Pollex
6个回答

16
虽然C++不允许这样做,但它允许你做非常类似的事情:test << s0 << s1; 然而,不要这样做! 如果我看到test.push(s0),我知道它的作用,甚至不用查看test的类型。如果我看到test << s0 << s1;,我会认为test是一个被写入的流。

以下是我认为在重载运算符时应遵循的三个基本规则:

  1. 尽管有明显的相反证据,但只有极少数情况适合使用运算符重载。因此,在其核心上,首要规则是:不要这样做。这可能看起来很奇怪,但原因是除非在应用程序域中使用运算符的用途是众所周知且无争议的,否则实际上很难理解运算符应用背后的语义。与流行的信仰相反,这种情况几乎从未发生过。每当运算符的含义不明显且没有争议时,都不应该进行重载。而是提供一个具有精心选择的名称的函数。
  2. C++对重载运算符的语义没有太多限制。编译器将高兴地接受实现二元+运算符更改其右操作数的代码。然而,这样的运算符的用户永远不会怀疑表达式a + b会更改b的值。这就是为什么第二条运算符重载规则说:始终坚持运算符的众所周知的语义。(这又预设了语义是无争议的;请参见前一条规则。)
  3. 最后,请始终记住,运算符与彼此和其他操作相关联。如果您的类型支持a + b,则用户将希望能够调用a + = b。如果它支持前缀递增++a,则他们也会期望a ++起作用。如果他们可以检查是否a < b,则他们几乎肯定也希望能够检查是否a > b。如果他们可以复制构造您的类型,则他们希望赋值也能正常工作。因此,运算符重载的第三个规则提醒您:始终提供一组相关操作中的所有操作。

与所有这样的规则一样,确实存在例外。有时人们偏离这些规则并且结果并不是糟糕的代码,但这种偏差很少见。至少,我见过的100个这样的偏差中,99个是没有道理的。不过,也可能是1000个中有999个。所以最好遵守这些规则。
所以,以防我的表述不够清楚:对于你问题中的代码,偏离这些规则非常糟糕。

你应该给诺基亚/ Trolltech 打电话,告诉他们使用 Qt 的代码是难以阅读的 ;-p << 表示“添加到集合”的含义不如 << 表示“写入流”那么清晰和无可争议,但它们大致相同。试图将这个含义强加到用户代码的标准库中是我发现有问题的地方...... - Steve Jessop
插入到流中,插入到集合中。这是同一件事。 - Puppy
@DeadMG:这取决于集合,我希望>>返回使用<<插入的最后一个元素,对于set,你如何做到这一点? - Matthieu M.
@DeadMG:对于队列/栈,我能理解,对于向量/列表,我同意这有点模糊,因为你可以从两端插入/检索。 - Matthieu M.
@Steve:如果某个流行库的供应商成功推出了类似这样的东西,他们可能会违反规则#1。毕竟,这就是Jerry Schwarz在滥用<<>>进行流处理时所做的事情,以及标准委员会在其string中使用+的做法(其中a+bb+a不同-哦,恐怖!)现在这已经成为了惯例。但是,当你作为一个开发者(而且还是新手),做这个事情时,你很难违反规则#1。 - sbi
显示剩余10条评论

3

首先,你提到的“隐式”入队项目的示例没有提及队列 - 你是指类似以下内容吗:

test << s0 << s1 << s2 << s3;

如果需要的话,这是“可能的”,但我不建议这样做。它并不能显著提高可读性。如果你真的想要它,可以在头部放置以下代码,并在需要此行为的地方引用它:
template<typename T>
std::queue<T> &operator<<(std::queue<T> &q, const T &v)
{
    q.push(v);
    return q;
}

请注意,由于C++的优先级规则,相反的顺序 - s0 >> s1 >> s2 >> test - 是不可能的。

2
考虑到 T==std::string,我对参数相关查找(Koenig Lookup)有点紧张。 - MSalters
1
MSalters是正确的,为了使ADL在从定义运算符的不同命名空间调用时正常工作,运算符应该在std命名空间中定义,但这在标准中是不允许的(允许现有函数/算法的特化,但不允许添加新操作)。 - David Rodríguez - dribeas
为什么不允许在C++中实现新的运算符?我可能会考虑实现运算符"=>". 从技术上讲,在C++中禁止实现这样的字符串序列s0 => s1 => s2 => .....,并且该运算符应将这些字符串存储在队列中。 - sami
@Steve:这是一个很好的反例!将“整个重点”替换为“一般情况下”... - Oliver Charlesworth
@sami 有些语言,例如Haskell,实际上允许定义新的运算符 - 不过,C++本身就已经很难解析,如果还允许用户定义自己的运算符,那将更加困难。 - bdonlan
显示剩余2条评论

3

我并不建议这样做,但如果你确实认为需要提供那个语法,你可以编写一个插入器适配器...

template <typename C>
class inserter_type {
public:
   typedef C container_type;
   typedef typename container_type::value_type value_type;

   explicit inserter_type( container_type & container ) : container(container) {}
   inserter_type& operator<<( value_type const & value ) {
      container.push( value );
      return *this;
   }
private:
   container_type & container;
};
template <typename C>
inserter_type<C> inserter( C & container ) {
   return inserter_type<C>(container);
}
int main() {
   std::queue<std::string> q;
   inserter(q) << "Hi" << "there";
}

2
我也不会这样做,但那是一个非常优雅的解决方案! - carlsborg

2

Qt 容器的工作方式与此完全相同:

QStringList list;
list << "Sven" << "Kim" << "Ola";

QVector<QString> vect = list.toVector();
// vect: ["Sven", "Kim", "Ola"]

如果您希望与STL容器一起使用,您需要自己编写运算符重载,但显然您不能向std命名空间添加操作。


说起来容易做起来难...你不能打破std命名空间以添加新的运算符(标准中也没有以queue为第一个参数的运算符),而在std命名空间之外定义运算符可能会涉及到ADL,这很棘手。 - David Rodríguez - dribeas
@David:你可以在全局命名空间中实现该运算符。 - sbi

0

如果您想要这样做,通常会表达为

test << s0 << s1 << s2 << s3;

通过在队列类上适当定义重载的<<运算符,可以实现这一点。但我不知道为什么这比简单的一系列test.push()调用更好。

因为一次添加多个项目时更易读,并且从流使用中很熟悉。 - teukkam
是的,我有这个想法。但我不想以那种方式实现它。想象一下,您有几个字符串应该添加到队列中。用户不一定要知道背后的机制如何工作。我只是举了一个例子,打了操作符“>>”,但它也可以是另一个操作符。也许是“=>”。我能否实现这样的操作符“=>”,它读取字符串序列的链接值并将其存储在队列中? - sami

0

我同意大多数答案,告诉你可以这样做,只要包括队列对象,并且我也同意可读性会受到影响,因为这是一个非常不标准的行为。

然而,我在想。如果你尝试像这样做:

queue test;
string s0("s0"), s1("s1");

test.push(s0).push(s1);

这可以非常简单地实现,仍然可以给您正确的可读性(因为push的含义是众所周知的),并保持您的代码简洁(这似乎是您的主要目标)。

要实现它,您只需要扩展队列类(或编写自己的队列类,该类将依次包装STL队列对象)。


不好的想法。std::queue已经在标准中定义,而且push方法不会返回队列的引用,因此您无法执行方法链接。另外请注意,虽然在技术上可以编辑标准头文件以提供对此的支持,但这将把符合标准的实现变成一个不可移植的非标准库,这绝不能被视为一个好主意。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接