# C++ヒッチハイクガイド
プログラミング言語の個々の機能の解説を理解するためには、まず言語の全体像を掴まなければならない。この章ではC++のさまざまなコードをひと通り観光していく。ここではコードの詳細な解説はしない。
# 最小のコード
以下はC++の最小のコードだ。
int main(){}
暗号のようなコードで訳がわからないが、これが最小のコードだ。main
というのはmain関数
のことだ。C++ではプログラムの実行はmain
関数から始まる。
ソースコードにコメントを記述して、もう少しわかりやすく書いてみよう。
int // 関数の戻り値の型
main // 関数名
() // 関数の引数
{ // 関数の始まり
// 実行される処理
} // 関数の終わり
//
から行末まではコメントだ。コメントには好きなことを書くことができる。
このコードと1つ前のコードは、コメントの有無を別にすれば何の違いもない。このコードで使っている、int
とかmain
とか記号文字の1つ1つをトークン(token)と呼ぶ。C++ではトークンの間に空白文字や改行文字をいくら使ってもよい。
なので、
int main(){ }
と書くこともできるし、
int main ( ) { }
と書くこともできるし、紙に印刷する都合上とても読みづらくなるかもしれないが
int
main
(
)
{
}
と書くこともできる。
ただし、トークンの途中で空白文字や改行文字を使うことはできない。以下のコードは間違っている。
i
nt ma in(){}
# 標準出力
// helloと改行を出力するプログラム
int main()
{
std::cout << "hello"s ;
}
標準出力はプログラムの基本だ。C++で標準出力する方法はいくつもあるが、<iostream>
ライブラリを利用するものが最も簡単だ。
std::cout
は標準出力を使うためのライブラリだ。
<<
はoperator <<
という演算子だ。C++では演算子にも名前が付いていて、例えば+
はoperator +
となる。<<
も演算子の一種だ。
"hello"s
というのは文字列で、二重引用符で囲まれた中の文字列が標準出力に出力される。
セミコロン;
は文の区切り文字だ。C++では文の区切りは明示的にセミコロンを書く必要がある。ほかの言語では改行文字を文脈から判断して文の区切りとみなすこともあるが、C++では明示的に文の区切り文字としてセミコロンを書かなければならない。
セミコロンを書き忘れるとエラーとなる。
int main()
{
// エラー! セミコロンがない
std::cout << "error"s
}
複数の文を書いてみよう。
int main()
{
std::cout << "one "s ;
std::cout << "two "s ;
std::cout << "three "s ;
}
C++はほかの多くの言語と同じように、逐次実行される。つまり、コードは書いた順番に実行される。そして標準出力のような外部への副作用は、実行された順番で出力される。このコードを実行した結果は以下のとおり。
one two three
"three two one "
や"two one three "
のような出力結果にはならない。
C++を含む多くの言語でa + b + c
と書けるように、operator <<
もa << b << c
と書ける。operator <<
で標準出力をするには、左端はstd::cout
でなければならない。
int main()
{
std::cout << "aaa"s << "bbb"s << "ccc"s ;
}
出力はaaabbbccc
となる。
# 文字列
二重引用符で囲まれた文字列を、文字どおり文字列
という。文字列には末尾にs
が付くものと付かないものがある。これには違いがあるのだが、わからないうちはs
を付けておいた方が便利だ。
int main()
{
// これは文字列
std::cout << "hello"s ;
// これも文字列、ただし不便
std::cout << "hello" ;
}
文字列リテラルの中にバックスラッシュを書くと、エスケープシーケンスとして扱われる。最もよく使われるのは改行文字を表す\n
だ。
int main()
{
std::cout << "aaa\nbbb\nccc"s ;
}
これは以下のように出力される。
aaa
bbb
ccc
バックスラッシュを文字列で使いたい場合は\\
と書かなければならない。
int main()
{
std::cout << "\\n is a new-line.\n"s ;
}
文字列は演算子operator +
で「足す」ことができる。「文字列を足す」というのは、「文字列を結合する」という意味だ。
int main()
{
std::cout << "hello"s + "world"s ;
}
# 整数と浮動小数点数
iostream
は文字列のほかにも、整数や浮動小数点数を出力できる。さっそく試してみよう。
int main()
{
std::cout
<< "Integer: "s << 42 << "\n"s
<< "Floating Point: "s << 3.14 ;
}
-123
や0
や123
といった数値を整数という。3.14
のような数値を浮動小数点数という。
数値を扱えるのだから、計算をしてみたいところだ。C++は整数同士の演算子として、四則演算(+-*/
)や剰余(%
)をサポートしている。
int main()
{
std::cout
<< 3 + 5 << " "s << 3 - 5 << " "s
<< 3 * 5 << " "s << 3 / 5 << " "s
<< 3 % 5 ;
}
演算子は組み合わせて使うこともできる。その場合、演算子*/%
は演算子+-
よりも優先される。
int main()
{
// 7
std::cout << 1 + 2 * 3 ;
}
この場合、まず2*3
が計算され6となり、1+6
が計算され7
となる。
1+2
の方を先に計算したい場合、括弧()
で囲むことにより、計算の優先度を変えることができる。
int main()
{
// 9
std::cout << (1 + 2) * 3 ;
}
これは1+2
が先に計算され3
となり、3*3
が計算され9
となる。
浮動小数点数同士でも四則演算ができる。剰余はできない。
int main()
{
std::cout
<< 3.5 + 7.11 << " "s << 3.5 - 7.11 << " "s
<< 3.5 * 7.11 << " "s << 3.5 / 7.11 ;
}
では整数と浮動小数点数を演算した場合どうなるのだろう。さっそく試してみよう。
int main()
{
std::cout << 1 + 0.1 ;
}
結果は1.1
だ。整数と浮動小数点数を演算した結果は浮動小数点数になる。
そういえばC++には文字列もあるのだった。文字列と文字列は足すことができる。数値と数値も足すことができる。では数値と文字列を足すとどうなるのだろう。
int main()
{
std::cout << 1 + "234"s ;
}
この結果はエラーになる。
いや待て、C++には末尾にs
を付けない文字列もあるのだった。これも試してみよう。
int main()
{
std::cout << 1 + "234" ;
}
結果はなんと34
になるではないか。C++では謎の数学により1 + "234" = "34"
であることが判明した。この謎はいずれ解き明かすとして、いまは文字列には必ず末尾にs
を付けることにしよう。その方が安全だ。
# 変数(variable)
さあどんどんプログラミング言語によくある機能を見ていこう。次は変数だ。
int main()
{
// 整数の変数
auto answer = 42 ;
std::cout << answer << "\n"s ;
// 浮動小数点数の変数
auto pi = 3.14 ;
std::cout << pi << "\n"s ;
// 文字列の変数
auto question = "Life, The Universe, and Everything."s ;
std::cout << question ;
}
変数はキーワードauto
に続いて変数名を書き、=
に続いて値を書くことで宣言できる。変数の宣言は文なので、文末にはセミコロンが必要だ。
auto 変数名 = 値 ;
変数名
はキーワード、アンダースコア(_
)で始まる名前、アンダースコア2つ(__
)を含む名前以外は自由に名付けることができる。
変数の最初の値は、= 値
の代わりに(値)
や{値}
と書いてもよい。
int main()
{
auto a = 1 ;
auto b(2) ;
auto c{3} ;
}
この=
, ()
, {}
による変数の初期値の指定を、初期化
という。
変数は使う前に宣言しなければならない。
int main()
{
// エラー、名前xは宣言されていない
std::cout << x ;
auto x = 123 ;
}
変数の値は初期化したあとにも演算子=
で変更できる。これを代入
という。
int main()
{
// 変数の宣言
auto x
// 初期化
= 123 ;
// 123
std::cout << x ;
// 代入
x = 456 ;
// 456
std::cout << x ;
// もう一度代入
x = 789 ;
// 789
std::cout << x ;
}
代入演算子operator =
は左辺に変数名を、右辺に代入する値を書く。面白いこととして、右辺には代入する変数名そのものを書ける。
int main()
{
auto x = 10 ;
x = x + 5 ;
// 15
std::cout << x ;
}
operator =
は「代入」という意味で、「等号」という意味ではないからだ。x=x+5
は、「x
とx+5
は等しい」という独創的な数学上の定義ではなく、「変数x
に代入前の変数x
の値に5を加えた数を代入する」という意味だ。
変数のいまの値に対して演算した結果を変数に代入するという処理はとてもよく使うので、C++にはx = x + a
と同じ意味で使える演算子、operator +=
もある。
int main()
{
auto x = 1 ;
// x = x + 5と同じ
x += 5 ;
}
operator +=
と同様に、operator -=
, operator *=
, operator /=
, operator %=
もある。
C++の変数は、専門用語を使うと「静的型付け」になる。静的型付けと対比されるのが「動的型付け」だ。もっと難しく書くと、動的型付け言語の変数は、C++で言えば型情報付きのvoid *
型の変数のような扱いを受ける。
C++の変数には型
がある。型
というのは値の種類を表す情報のことだ。
例えば、以下は変数が動的型付けの言語JavaScriptのコードだ。
var x = 1 ;
x = "hello" ;
x = 2 ;
JavaScriptではこのコードは正しい。変数x
は数値型であり、文字列型に代わり、また数値型に戻る。
C++ではこのようなコードは書けない。
int main()
{
auto x = 1 ;
// エラー
x = "hello"s ;
x = 2 ;
}
C++では、変数x
は整数型であり、文字列型に変わることはない。整数型の変数に文字列型を代入しようとするとエラーとなる。
C++では型に名前が付いている。整数型はint
、浮動小数点数型はdouble
、文字列型はstd::string
だ。
int main()
{
// iはint型
auto i = 123 ;
// dはdouble型
auto d = 1.23 ;
// sはstd::string型
auto s = "123"s ;
}
実は変数の宣言でauto
と書く代わりに、具体的な型を書いてもよい。
int main()
{
int i = 123 ;
double d = 1.23 ;
std::string s = "123"s ;
}
整数型(int
)と浮動小数点数型(double
)はそれぞれお互いの型の変数に代入できる。ただし、変数の型は変わらない。単に一方の型の値がもう一方の型の値に変換されるだけだ。
int main()
{
// 浮動小数点数型を整数型に変換
int a = 3.14 ;
// 3
std::cout << a << "\n"s ;
// 整数型を浮動小数点数型に変換
double d = 123 ;
// 123
std::cout << d ;
}
浮動小数点数型を整数型に変換すると、小数部が切り捨てられる。この場合、3.14
の小数部0.14
が切り捨てられ3
となる。0.9999
も小数部が切り捨てられ0
になる。
int main()
{
int i = 0.9999 ;
// 0
std::cout << i ;
}
整数型を浮動小数点数型に変換すると、値を正確に表現できる場合はその値になる。正確に表現できない場合は近い値になる。
int main()
{
double d = 1234567890 ;
// 正確に表現できるかどうかわからない
std::cout << d ;
}
整数型と浮動小数点数型の挙動についてはあとの章で詳しく解説する。また、これ以外にも型はいくらでもあるし、読者が新しい型を作り出すこともできる。これもあとの章で詳しく解説する。
# 関数(function)
「変数ぐらい知っている。さっさと教えてもらいたい。どうせC++の関数は書きづらいのだろう」と考える読者の皆さん、お待たせしました。こちらがC++の関数でございます。
int main()
{
// 関数
auto print = [](auto x)
{
std::cout << x << "\n"s ;
} ;
// 関数呼び出し
print(123) ;
print(3.14) ;
print("hello") ;
}
C++では関数も変数として扱える。auto print =
までは変数だ。変数の初期化として関数を書いている。より正確にはラムダ式と呼ばれる関数を値として書くための文法だ。
ラムダ式は以下のような文法を持つ。
[] // ラムダ式導入部
() // 引数
{} // 本体
ラムダ式は[]
で始まり、()
の中に引数を書き、{}
の中の文が実行される。
例えば以下は引数を2回標準出力する関数だ。
int main()
{
auto twice = [](auto x)
{
std::cout << x << " "s << x << "\n"s ;
} ;
twice(5) ;
}
引数はauto 引数名
で受け取れる。引数を複数取る場合は、カンマ,
で区切る。
int main()
{
auto print_two = []( auto x, auto y )
{
std::cout << x << " "s << y << "\n"s ;
} ;
print_two( 1, 2 ) ;
print_two( "Pi is", 3.14 ) ;
}
引数を取らないラムダ式を書く場合は、単に()
と書く。
int main()
{
auto no_args = []()
{
std::cout << "Nothing.\n" ;
} ;
no_args() ;
}
関数は演算子operator ()
を関数の直後に書いて呼び出す。これが演算子であるというのは少し不思議な感じがするが、C++では紛れもなく演算子だ。operator +
とかoperator -
などと同じ演算子だ。
int main()
{
// 何もしない関数
auto func = [](){} ;
// operator ()の適用
func() ;
// これもoperator ()
func ( ) ;
}
演算子operator ()
は、ラムダ式そのものに対して適用することもできる。
int main()
{
// 変数fをラムダ式で初期化
auto f = [](){} ;
// 変数fを関数呼び出し
f() ;
// ラムダ式を関数呼び出し
[](){}() ;
}
このコードを見ると、operator ()
が単なる演算子であることがよくわかるだろう。[](){}
がラムダ式でその直後の()
が関数呼び出し演算子だ。
関数は値を返すことができる。関数から値を返すには、return文
を使う。
int main()
{
auto plus = []( auto x, auto y )
{ return x + y ; } ;
std::cout
<< plus( 1, 2 ) << "\n"s
<< plus( 1.5, 0.5 ) << "\n"s
<< plus( "123"s, "456"s) ;
}
関数はreturn
文を実行すると処理を関数の呼び出し元に返す。
int main()
{
auto f = []()
{
std::cout << "f is called.\n" ;
return 0 ; // ここで処理が戻る
std::cout << "f returned zero.\n" ;
} ;
auto result = f() ;
}
これを実行すると以下のようになる。
$ make
f is called.
return
文以降の文が実行されていないことがわかる。
# 本当の関数
実はラムダ式は本当のC++の関数
ではない。本当の関数
はとても書きづらいので心して読むべきだ。
読者は本書の冒頭で使ったmain関数
という言葉を覚えているだろうか。覚えていないとしても、サンプルコードに必ずと言っていいほど出てくるmain
という名前は気になっていたことだろう。
int main(){}
これを見ると、聡明な読者はラムダ式と似通ったところがあることに気付くだろう。
[](){}
末尾の(){}
が同じだ。これは同じ意味だ。()
は関数の引数で、{}
は関数の本体だ。
では残りの部分はどうだろうか。int
は関数の戻り値の型、main
は関数の名前だ。
C++の本当の関数は以下のような文法で定義される。
int // 戻り値の型
main // 関数名
() // 関数の引数
{} // 関数の本体
試しに、int
型の引数を2つ取り足して返す関数plus
を書いてみよう。
int plus( int x, int y )
{
return x + y ;
}
int main()
{
auto x = plus( 1, 2 ) ;
}
では次に、double
型の引数を2つ取り足して返す関数plus
を書いてみよう。
double plus( double x, double y )
{
return x + y ;
}
int main()
{
auto x = plus( 1.0, 2.0 ) ;
}
最後のstd::string
型の引数を2つ取り足して返す関数plus
は読者への課題とする。
これがC++の本当の関数だ。C++の関数では、型をすべて明示的に書かなければならない。型を間違えるとエラーだ。
しかも、C++の関数は、戻り値の型を正しく返さなければならない。
int f()
{
// エラー、return文がない
}
もし、何も値を返さない関数を書く場合は、どの値でもないという特別な型、void
型を関数の戻り値の型として書かなければならないという特別なルールまである。
void f()
{
// OK
}
ただし、戻り値の型については、具体的な型の代わりにauto
を書くこともできる。その場合、return
文で同じ型さえ返していれば、気にする必要はない。
// void
auto a() { }
// int
auto b() { return 0 ; }
// double
auto c() { return 0.0 ; }
// std::string
auto d() { return ""s ; }
// エラー
// return文の型が一致しない。
auto e()
{
return 0 ;
return 0.0 ;
}