- C++のHello Worldでよく使われる
cout <<は、ビットシフト演算子を関数として再定義した演算子オーバーロードの仕組みで動いている。 - 入門書の
cout <<は名前空間・ストリーム・演算子オーバーロードを一行に詰め込んでいるため、初学者だと概念を誤解やすく「クセのある書き方」と認識しにくい。 - C++23で導入された
std::printlnを使えば、こうした複雑さを意識せず出力を書ける。
1. もう一つのC++のHello World
プログラミングを学ぶとき、最初に書くコードはほぼ決まっています。
Hello Worldです1。
C++23では、Hello Worldプログラムは、このように書けます。
import std;
int main() {
std::println("Hello, World!");
}Code language: C++ (cpp)
ところが、一般的なC++の入門書では次のように書かれます。
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
}Code language: C++ (cpp)
これは、Stroustrupが1985年に出版した「The C++ Programming Language」に由来する説明です。
このコードは、オブジェクトとメソッドだけでは読めません。
C++のcout <<は何をしているのか、初めて見たときには見当がつきにくいです。
2. <<の正体
cout << "Hello" の<<は、演算子オーバーロードで定義された関数呼び出しです。
もともとのC言語では、<<はビットシフト演算子でした。
int x = 1;
int y = x << 3; // 1 を左に3ビットシフト → 8Code language: C++ (cpp)
それを、C++では関数として再定義しています。operator<<という名前の関数がiostreamの中に定義されており、<<と書くとその関数が呼ばれるのです。
通常の関数呼び出しの形に書き直すと、こうなります。
std::operator<<(std::cout, "Hello, World!");
std::operator<<(std::cout, std::endl);Code language: C++ (cpp)
std::coutはオブジェクト、std::operator<<は関数です。iostreamをインクルードすることで、この両方が使えるようになります。
また、cout << "Hello" << " World" のように連鎖できるのは、operator<<がstd::ostream&を返すからです2。
戻り値のストリームに対して再びoperator<<を呼べるので、構造としてはネストした関数呼び出しになっています。
operator<<(operator<<(cout, "Hello"), " World");Code language: C++ (cpp)
ストリームという抽象概念を演算子で表現する、C++の特徴的な設計です。
2.1. Stroustrupのストリーム設計
C++は1980年代に、Cとの互換性を強く意識して設計されました。
Stroustrupは、1984年に標準入出力のstdioに変わるライブラリとして、iostreamを実装しました3。
このとき、入出力を「ストリーム」という概念で設計し直し、<<という演算子でそれを表現しました。
この設計の利点は、coutもofstream(ファイル出力)もstringstream(文字列バッファ)も、同じ<<インターフェースで扱えることです。operator<<を追加で定義するだけで、自作の型を出力できるようにもなります。<<という記号が選ばれた背景には、Bell LabsのDoug McIlroyの提案があります4。

ターミナルシェルでのリダイレクトに似ているよね。
3. 概念が詰まっている
ただし初学者にとっては、最初の一行に複数の概念が詰まりすぎています。
名前空間、ストリームオブジェクト、演算子オーバーロード、テンプレートベースの設計。
入門書でよく見るusing namespace std;も、実務のコードでは名前衝突を招くとして推奨されない書き方です5。
こうした事情が重なって、Hello Worldが何をしているのか見えにくくなっています。
また、endlは改行だけでなくバッファのフラッシュも行うため、'\n'より処理が重くなります6。
C++のエッセンスがつまったコードですが、入門としては実務に向かないクセを覚えてしまうという側面もあります。
3.1. 素直なオブジェクト指向で書き直すなら
operator<<の呼び出しを明示した形でConsoleクラスを作ると、動作原理がそのまま読めるコードになります。
#include <iostream>
class Console {
public:
void println(const char* s) {
std::operator<<(std::cout, s);
std::operator<<(std::cout, std::endl);
}
};
int main() {
Console out;
out.println("Hello, World!");
}Code language: C++ (cpp)
iostreamの中にcoutというオブジェクトとoperator<<という関数があること、Consoleクラスはそれをラップしていること、mainの中でオブジェクトを作ってメソッドを呼ぶこと。
この三つがそのまま読めます。
少しぎこちない書き方ではありますが、最初の一行で演算子オーバーロードとストリームを同時に学ぶよりは、概念の負荷が小さくなります。
3.2. C++23のstd:println
とはいえ、coutはクセが強いです。
C++23ではstd::printlnが導入され、cout <<を使わずに書けるようになりました7。
import std;
int main() {
std::println("Hello, World!");
}Code language: C++ (cpp)
Pythonのprint関数に近い使い方になり、現代的になりました。
4. Hello Worldが教えること
Hello Worldは単なる動作確認ではなく、その言語の設計思想が凝縮されています。
何を重視した言語かが、Hello Worldの時点で既に現れています。
C++は柔軟な表現力を優先し、演算子の再定義まで含めた設計を標準に採用しました。
C++のHello Worldが少しわかりにくいのは、C++が「そういう柔軟な言語」だからです。
- Hello Worldというプログラム例はBrian Kernighanが1972年にBell Labsで言語Bのチュートリアルとして書いたのが起源です。1974年の社内文書「Programming in C: A Tutorial」を経て、1978年にKernighanとDennis Ritchieが出版した「The C Programming Language」で広く知られるようになりました。 – Wikipedia: “Hello, World!” program
- 戻り値として同じストリームオブジェクトへの参照を返すことで、
operator<<(operator<<(cout, "Hello"), " World")のようにネストした呼び出しが成立します。これはメソッドチェーンと同じ原理です。 – cppreference: std::basic_ostream::operator<< - Bjarne Stroustrupは1984年にC標準ライブラリの型安全な代替として最初のストリームI/Oライブラリを実装しました。その後テンプレート化や名前空間への移動など改良が重ねられ、1998年の標準化でヘッダが
<iostream.h>から<iostream>に変更されました。 – Wikipedia: Input/output (C++) - 出力演算子に
<<を使うアイデアはDoug McIlroyがUNIXシェルのI/Oリダイレクト演算子(>や>>)にヒントを得て提案しました。=や<なども候補でしたが、=は結合方向の問題があり、<と>は大小比較の意味が強すぎて混乱を招くとして退けられました。 – Qiita: std::cout << “a very short history of iostream” using namespace std;を使うとstd::countやstd::listなど標準ライブラリの名前が修飾なしで使えてしまい、自作の変数名や関数名と衝突する可能性があります。cppreferenceも「std::を省略するのは習慣的に避けることを推奨する」と述べています。 – cppreference: Your first program in C++std::endlは改行文字を出力したうえでストリームバッファをフラッシュします。フラッシュはシステムコールを伴うため、ループ内など頻繁に呼ぶ場面ではパフォーマンスが大きく低下することがあります。単に改行したいだけなら'\n'で十分です。clang-tidyのperformance-avoid-endlチェックでも同様の指摘がされています。 – clang-tidy: performance-avoid-endl- C++23で追加された
std::printlnを使うと、import std; int main() { std::println("Hello, World!"); }と書けます。import std;はC++23のモジュール機能で、標準ライブラリ全体を一行でインポートできます。std::printlnはPythonのprint関数に近い感覚で使え、改行も自動で行われます。 – ModernesCpp: C++23 std::print and std::println