【问题标题】:need some kind of operator.. c++需要某种运算符.. c++
【发布时间】:2010-09-02 11:07:35
【问题描述】:

我想在队列中存储一个字符串序列。如果我使用成员函数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 ;

有什么办法吗?

【问题讨论】:

  • 我为您修正了代码格式。这不是通过 [CODE] 标记完成的,而是通过将行缩进四个空格(单击文本字段上方的代码按钮将为您完成)。

标签: c++


【解决方案1】:

虽然 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 人。所以你最好遵守这些规则。

所以以防我不够清楚:对于您问题中的代码,偏离这些规则是非常糟糕的。

【讨论】:

  • 你应该打电话给诺基亚/奇趣科技,让他们知道使用 Qt 的代码是不可读的;-p << 意思是“添加到集合”比<< 意思是“写流”,但它在那里或附近。试图在用户代码中将这个含义附加到标准库上是我觉得有问题的......
  • 如果标准将 > 定义为提取,那么它绝对是尽可能清晰和无可争议的,因为它实际上是用语言规则编写的。我认为 > 作为从更多容器中插入/提取而不仅仅是流是可以的。
  • @DeadMG:流是容器的语言规则当然不是写的——它们不是。我不认为这是从“流插入”到“添加到集合”的巨大飞跃,但我也不认为这是同一件事。
  • 插入流,插入集合。是一样的。
  • @DeadMG:这取决于集合,我希望>> 返回使用<< 插入的最后一个元素,您如何使用set 做到这一点?
【解决方案2】:

首先,你的“隐式”排队项目的例子没有提到队列 - 你的意思是这样的:

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 &gt;&gt; s1 &gt;&gt; s2 &gt;&gt; test - 不可能

【讨论】:

  • 考虑到 T==std::string,我对 Argument Dependent Lookup (Koenig Lookup) 有点紧张。
  • MSalters 是对的,为了让 ADL 在从不同于定义运算符的命名空间中调用时正常工作,应在 std 命名空间中定义运算符,但这在标准(允许对现有函数/算法进行专门化,但不允许添加新操作)。
  • 为什么不允许在 c++ 中实现 new 运算符。我可能会考虑实现运算符“=>”。从技术上讲,c++ 中禁止实现这样的字符串序列 s0 => s1 => s2 => ..... 并且操作员应将这些字符串存储在队列中?
  • @sami:可能是因为编译器很难(并且可能不可能)明确地解析您的代码。此外,标准运算符重载的重点是使复杂类型“看起来和感觉”像原始类型(例如,矩阵加法看起来像标量加法)。允许用户定义的运算符在这里没有帮助!
  • @Oli:我不认为这是运算符重载的全部点。如果是这样,那么流就不会像它们那样使用operator&gt;&gt;operator&lt;&lt;。但我同意 C++ 不允许你发明运算符的原因是它会干扰解析表达式的规则。事实上,即使是标记化也可能变得困难,正如 space-needed-in-vector&lt;vector&lt;int&gt; &gt; 问题所示。当您发明一个运算符时,您还必须为 (1) 标记化 在预处理器和编译器中 和 (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";
}

【讨论】:

  • 我也不会这样做,但这是一个非常优雅的解决方案!
【解决方案4】:

Qt 容器的工作原理完全一样:

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

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

如果您想使用 STL 容器,您需要自己编写重载运算符,但显然您不能将操作添加到 std 命名空间。

【讨论】:

  • 说起来容易做起来难......你不能闯入std命名空间来添加新的运算符(并且没有运算符将queue作为标准中的第一个参数),使用 ADL 在 std 命名空间之外定义运算符可能会很棘手。
  • @David:您总是可以在全局命名空间中实现该运算符。
【解决方案5】:

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

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

通过适当定义队列类上的重载operator&lt;&lt;。我不知道为什么这会比一系列简单的test.push() 调用更好。

【讨论】:

  • 因为一次添加一堆项目时可读性更高,并且从流使用中熟悉。
  • 是的,我有这个想法。但我不想那样实现它。想象一下,您有几个字符串应该添加到队列中。用户不一定要知道其背后的机制。例如,我键入了运算符>>,但它可能是另一个运算符。也许=>。我可以实现这样的运算符“=>”,它读取字符串序列的链接值并将其存储在队列中。
【解决方案6】:

我同意大多数答案,这些答案告诉您,只要您包含队列对象,您就可以这样做,而且我也同意可读性受到影响,因为它是一种非常非标准行为.

不过,我还是想知道。如果您尝试以下操作会怎样:

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

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

这可以非常简单地实现,仍然会给你正确的可读性(因为push 的含义很好理解),并保持你的代码简洁(这似乎是你的主要目标)。

要实现它,您所要做的就是扩展 Queue 类(或编写您自己的队列类,它会依次包裹一个 STL Queue 对象)。

【讨论】:

  • 坏主意。 std::queue 是在标准中定义的,push 方法返回对队列的引用,因此不能执行方法链接。另请注意,虽然在技术上可以编辑标准头文件以提供对此的支持,但您会将符合标准的实现分解为不可移植的非标准库,这绝不是一个好主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-01
  • 2012-10-25
  • 2012-03-09
  • 1970-01-01
相关资源
最近更新 更多