【问题标题】:C++ Standard Library: How to write wrappers for cout, cerr, cin and endl?C++ 标准库:如何为 cout、cerr、cin 和 endl 编写包装器?
【发布时间】:2011-02-22 04:48:58
【问题描述】:

我不喜欢using namespace std,但我也厌倦了必须在每个coutcincerrendl 前面输入std::。所以,我想给他们起这样更短的新名字:

// STLWrapper.h

#include <iostream>
#include <string>

extern std::ostream& Cout;
extern std::ostream& Cerr;
extern std::istream& Cin;
extern std::string&  Endl;

// STLWrapper.cpp

#include "STLWrapper.h"

std::ostream& Cout = std::cout;
std::ostream& Cerr = std::cerr;
std::istream& Cerr = std::cin;
std::string _EndlStr("\n");
std::string& Endl = _EndlStr;

这行得通。但是,上面有什么我遗漏的问题吗?有没有更好的方法来达到同样的效果?

【问题讨论】:

  • 这只有在你是一家单人公司并且没有其他人会阅读代码的情况下才可以。像这样的快捷方式只会使代码变得模糊,对于开发团队来说几乎不是一个好主意。
  • Martin:注意点。是的,当代码与其他人一起使用时,这可能不是一个好主意。
  • 此处定义的std::string&amp; Endl 与尝试刷新缓冲区的std::endl 具有不同的功能。

标签: c++ iostream


【解决方案1】:

Alex 为您提供了an answer 如何在语法上解决该问题。但是,我想指出关于这个问题的另外两个论点:

  1. 无论您使用的是 using 指令 (using namespace std) 还是其较小的邪恶姐妹,using 声明 (using std::cout),超载可能会导致令人讨厌的意外。输入std::花半个晚上调试 找出your code called std::distance() instead of your own distance() function 并不麻烦,只是因为你犯了一个小错误而std::distance() 不小心是更好的匹配。

  2. 一行代码只写一次,但是 - 根据其生命周期 - 它会被读取数十、数百甚至数千次。所以编写一行代码所花费的时间根本不重要重要的只是阅读和解释一行代码所花费的时间 >。即使用所有正确的std:: 写一行需要三倍的时间,如果它使读取速度仅快 10%,那仍然是值得的。
    所以重要的问题是:所有std:: 都在位的代码行更容易阅读和解释还是更难?来自another answer

    这里还有一个数据点:很多很多年前,我也曾经发现必须在标准库中的所有内容前加上 std:: 前缀很烦人。然后我在一个项目中工作,一开始就决定除了函数范围外,using 指令和声明都被禁止。你猜怎么了?我们大多数人花了几个星期才习惯编写前缀,几周后我们大多数人甚至同意它实际上使代码更具可读性。 (这是有原因的:你喜欢较短还是较长的散文是主观的,但前缀客观地增加了代码的清晰度。不仅是编译器,你也是,更容易看到引用了哪个标识符。)

    十年后,该项目发展到拥有数百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)函数范围 using 在项目中实际使用的频率。我搜索了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试,开发人员并没有发现 std:: 足够痛苦即使每 100kLoC 使用一次 using 指令即使在允许的情况下也是如此使用

    我认为你会发现的每本书和教程都跳过std:: 很遗憾,因为这会让人们习惯于以这种方式阅读代码。当我教 C++ 几年(在上述经验之后)时,我告诉我的学生我不想在他们的代码中看到任何 using 指令或声明。 (该规则的唯一例外是using std::swap,顺便说一句,您需要它才能让swap(a,b) 拾取命名空间std 之外的重载。)一旦他们习惯了它,他们就不会介意并且,当被问及此事时,他们说他们发现没有std:: 前缀的代码令人困惑。 有些人甚至将std:: 前缀添加到他们从没有它的书籍或教程中键入的代码中

底线:输入std:: 有什么难的地方,以至于每个人都为此而激动不已?到目前为止,我已经做了 15 年以上,我一点也不怀念using

【讨论】:

  • Sbi:我不得不承认,我很早就受到书籍或其他使用“使用”的来源的代码的影响。我可能受到 Java 和 Python 中类似指令的影响。在考虑了您的推理(并且没有更好的选择)之后,我决定修复代码以在所有地方使用 std:: 。感谢您花时间详细回复:-)
  • +1(我希望我能做更多)表示“一行代码被编写一次,但是 - 根据它的生命周期,它会被读取数十次、数百次甚至数千次。所以编写一行代码所花费的时间根本不重要,重要的是读取一行代码和解释代码所花费的时间。” -- 如此真实,但很难说服同事。
  • 哼,没看到。我也相信前缀可以增加清晰度,特别是当命名空间和子文件夹匹配时,这样你就知道哪个包含带来了那个特定的对象:)
  • @我们无能为力:auto 不是为了减少打字,而是为了创建您无法拼写名称的类型的变量。碰巧你可以用它来减少打字,但它添加的主要功能是能够写这个:auto lambda = [](int x){ return x*x; };,如果没有auto,这是不可能的。
  • @There:您可能已经意识到,在我标记为冒犯之后,您对 David 的最后两个 cmets 消失了。这不是我第一次看到这种情况发生在你身上。现在我将停止与您的讨论。我学会了尊重大卫的见解,我喜欢和他讨论,因为我从中学到了很多东西。 OTOH,您不是在讨论以获得新的见解,您只是在讨论以证明您的观点,因此如果讨论不按您的方式进行,您会生气。我可以做得很好,而不会浪费我的时间。祝你有美好的一天。
【解决方案2】:

为什么不

using std::cin;
using std::cout;

等等?然后在您的代码中,您可以使用cincout 等等,而不会意外地将所有其余的std 命名空间注入您的代码。

【讨论】:

  • [面对面] 我什至不知道“使用”可以与非命名空间实体一起使用!谢谢! :-)
  • @Ashwin: using namespace_name::identifier 被称为“命名空间声明”,而using namespace_name 被称为“命名空间指令”。
  • Sbi:非常感谢!我现在要回到书本上阅读有关“使用”的所有内容。
  • @Ashwin:一旦你阅读了using,它也可以用于将基类标识符带入派生类的范围,以便考虑重载决议,或使私有基的标识符在派生类的接口中可访问。但是,我还没有真正在野外看到过。
猜你喜欢
  • 2011-01-16
  • 2011-04-16
  • 1970-01-01
  • 2016-06-30
  • 1970-01-01
  • 2017-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多