#define print(msg) std::cout << msg << std::endl

3

我最近开始使用C++,感觉有一种强烈的渴望


#define print(msg) std::cout << msg << std::endl

这种方法在所有情况下都能正常工作吗?这是我所知道的唯一适用于msg中存在<<(例如"foo" << myInt)的公式。两种其他公式:

#define print(msg) std::cout << (msg) << std::endl // note: parens

也不是建议的答案

template<typename T>
void print(T const& msg) {
    std::cout << msg << std::endl;
}

在这种情况下,使用 endl 和使用 \n 来刷新输出的效率对我来说并不重要。

1
宏通常是不好的,特别是当它们可以被函数替代时。而且它并没有指示它是否打印新行或刷新缓冲区。实际上,将其内联也同样容易。 - chris
3
一般情况下,尽管不是内联的操作,但将内容打印到控制台仍然是一个非常慢的操作,因此你不会注意到有任何区别。 - Benjamin Lindley
3
这似乎毫无意义。如果你有一个类似可变参数的方式,比如print("Hello", 10, true, "world"),那么可能会有些意义... - Kerrek SB
2
你为什么有这种冲动?你认为它相比普通函数能给你带来什么好处? - Benjamin Lindley
1
投票重新开放。这远非无建设性。它也是真正经常被问到的问题。 - Potatoswatter
显示剩余19条评论
4个回答

5

既然您提到您最近刚开始使用 C++,我想向您展示一种更好的选择:

template<typename T>
void print(T const& msg)
{
    std::cout << msg << std::endl;
}

它接受一个任意类型的单一“msg”参数,并通过“std :: cout”将其流输出。正如评论中所提到的,std :: endl 不仅插入新行,而且还清空流。这类似于在“\n”上刷新printf。如果你只想要一个新行,你可能需要明确地这样做:
std::cout << msg << '\n';

4
"endl" 执行了一个不必要的刷新操作。如果你真的需要这个功能,最好使用 " << '\n' " 和 " << flush " 代替它。 - Potatoswatter
@1:你想一次性输出多个东西吗?你的编译器支持_C++11_吗? - K-ballo
@Potatoswatter:为什么cout << "\n" << flushcout << endl更好? - Keith Thompson
1
@KeithThompson 它表达了意图。(我从未见过需要手动刷新 cout 的情况。在读取 cin 之前,它会自动发生。如果您正在使用 scanfcout,那么您需要 endl。或者显示非常慢的程序输出,但状态消息应该发送到始终不带缓冲的 cerr。否则,它根本不会发生,所以这个问题是无意义的。) - Potatoswatter
1
我认为endl的功能已经足够出名了,老实说,如果你真的不在意是否刷新,就没有必要使用endl。用\n打字更少。 - chris
显示剩余2条评论

3

这是相当主观的,但你只需编写一次代码却要多次阅读它。代码的其他维护者希望能理解你所写的内容,因此当你需要输出信息时,就只需写 std::cout << msg << std::endl。不要试图让C++看起来像另一种语言。


2

仅供娱乐,这里提供两种C++11可变参数的print实现:一种在参数之间插入空格,另一种则不插入。(代码演示请参见ideone。)

#include <iostream>

namespace with_spaces {

namespace detail {
std::ostream& print(std::ostream& os) {
    return os;
}

template <typename T>
std::ostream& print(std::ostream& os, T&& t) {
    return os << std::forward<T>(t);
}

template <typename T, typename U, typename... Args>
std::ostream& print(std::ostream& os, T&& t, U&& u, Args&&... args) {
    return print(print(os, std::forward<T>(t)) << ' ', std::forward<U>(u), std::forward<Args>(args)...);
}
}

template <typename... Args>
void print(Args&&... args) {
    detail::print(std::cout, std::forward<Args>(args)...) << std::endl;
}
}

namespace without {
namespace detail {
std::ostream& print(std::ostream& os) {
    return os;
}

template <typename T>
std::ostream& print(std::ostream& os, T&& t) {
    return os << std::forward<T>(t);
}

template <typename T, typename... Args>
std::ostream& print(std::ostream& os, T&& t, Args&&... args) {
    return print(print(os, std::forward<T>(t)), std::forward<Args>(args)...);
}
}

template <typename... Args>
void print(Args&&... args) {
    detail::print(std::cout, std::forward<Args>(args)...) << std::endl;
}
}

#include <iomanip>

int main() {
    std::cout << std::boolalpha;
    with_spaces::print(1, "foo", new int(3), 0xFFFFFFFFFFULL, 42, 0 == 1);
    without::print(1, "foo", new int(3), 0xFFFFFFFFFFULL, 42, 0 == 1);
}

有趣的是,为了实现这个简单一行的宏所能做到的事情,需要编写大量的代码。


好家伙。到目前为止,这是唯一正确的答案,尽管它需要将内部的<<替换为,。我会坚持使用宏,但有一天我会阅读更多关于右值引用并弄清楚这里发生了什么。非常棒的答案,谢谢! - 1''
C++程序员对宏的不适感源于我们知道宏可能会破坏语言的语法,而破碎的语法会使代码难以阅读。print(a << 2)输出什么?10个程序员中有9个在看到你的程序时会猜错。当我们谈论你在学习时所做的事情时,这显然并不重要,但是当你在stackoverflow上发布有关预处理器宏的内容时,请不要感到惊讶,因为人们可能会对此进行批评。 - Casey

0

宏是广泛使用的,但不是好的实践方法(特别是在C++中),因为它们可以隐藏实际发生的事情,并使调试代码变得不可能。

宏还会绕过预处理器的类型检查,可能导致运行时问题。

如果您正在尝试优化速度,我建议在这里使用内联函数。


3
宏并不能绕过类型检查,它们完成文本替换后才执行类型检查。至于性能方面,由endl执行的I/O刷新操作将远远超过额外函数调用开销的数量级。 - Potatoswatter

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