Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
EzoeRyou authored Jul 19, 2019
2 parents 6a7d616 + 7561c0b commit 3f6c45a
Show file tree
Hide file tree
Showing 23 changed files with 4,544 additions and 3,509 deletions.
17 changes: 15 additions & 2 deletions 000-preface.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
#

本書はプログラミング経験者向けのC++入門書である
本書はプログラミングの経験はあるがC++は知らない読者を対象にしたC++を学ぶための本である。本書はすでに学んだことのみを使って次の知識を説明する手法で書かれた。C++コンパイラーをC++で書く場合、C++コンパイラーのソースコードをコンパイルする最初のC++コンパイラーをどうするかというブートストラップ問題がある。本書はいわばC++における知識のブートストラップを目指した本だ。これにより読者は本を先頭から読んでいけば、まだ学んでいない概念が突如として無説明のまま使われて混乱することなく読み進むことができるだろう

本書の対象読者は、すでに何らかの実用的なプログラミング言語を習得していることを想定し、プログラミングの初歩的な概念はすべて理解しているものとして説明しない。そのため、本書には、「変数は箱のようなものである」といったような説明は出てこない。ただし、主要なほかの言語とC++として特別に注意が必要な差は解説している
C++知識のブートストラップを意識した入門書の執筆はなかなかに難しかった。ある機能Xを教えたいが、そのためには機能Yを知っていなければならず、機能Yを理解するためには機能Zの理解が必要といった具合に、C++の機能の依存関係の解決をしなければならなかったからだ。著者自身も苦しい思いをしながらできるだけ今までに説明した知識のみを使って次の知識を教えるように牡蠣進めていった結果、意外な再発見をした。ポインターを教えた後はC++のほとんどの機能を教えることに苦労しなくなったのだ。結局C++では未だにポインターの機能は様々な機能の土台になっているのだろう


本書の執筆時点でC++は現在、C++20の規格制定に向けて大詰めを迎えている。C++20では`#include`に変わるモジュール、軽量な実行媒体であるコルーチン、高級なassert機能としてのコントラクトに加え、とうとうコンセプトが入る。ライブラリとしてもコンセプトを活用したレンジ、span、flat_mapなど様々なライブラリが追加される。その詳細は、次に本を出す機会があるならば江添亮の詳説C++17と似たようなC++20の参考書を書くことになるだろう。C++はまだまだ時代に合わせて進化する言語だ。


本書の執筆はGitHub上で公開した状態で行われた。

<https://github.com/EzoeRyou/cpp-intro>

本書のライセンスはGPLv3である。ただし、本書の著者近影はGPLv3ではなく撮影者が著作権を保持している。


本書の著者近影の撮影は、著者の古くからの友人でありプロのカメラマンである三浦大に撮影してもらった。

三浦大のWebサイト: <http://www.masarumiura.jp/>
2 changes: 1 addition & 1 deletion 002-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ GNU Makeはカレントディレクトリーにあるファイル`Makefile`の
~~~
$ touch source01 source02 source03
$ ls
Makefile source01 source03 source03
Makefile source01 source02 source03
$ make
cat source01 source02 source03 > source
cat source > program
Expand Down
1 change: 0 additions & 1 deletion 003-guide-to-c++.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ ccc
~~~cpp
int main()
{
//
std::cout << "\\n is a new-line.\n"s ;
}
~~~
Expand Down
4 changes: 2 additions & 2 deletions 004-debug-compile-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,11 @@ main.cpp:7:40: error: expected ‘;’ before ‘)’ token
~~~
std::cout << // 標準出力
f // 関数名
( // 空き括弧
( // 開き括弧
1+(2*3), // 第1引数
4-5, // 第2引数
6/(7-8) // 第3引数
) // 空き括弧に対応する閉じ括弧
) // 開き括弧に対応する閉じ括弧
) // ???
; // 終端文字
~~~
Expand Down
10 changes: 5 additions & 5 deletions 007-standard-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ int main()
double bmi = mass / (height*height) ;

// BMIの出力
std::cout << "BMI=" << bmi << "\n"s ;
std::cout << "BMI="s << bmi << "\n"s ;
}
~~~

Expand All @@ -44,7 +44,7 @@ BMI 状態
~~~cpp
int main()
{
// 身長1.6m
// 身長1.63m
double height = 1.63 ;
// 体重73kg
double mass = 73.0 ;
Expand All @@ -53,7 +53,7 @@ int main()
double bmi = mass / (height*height) ;

// BMIの出力
std::cout << "BMI=" << bmi << "\n"s ;
std::cout << "BMI="s << bmi << "\n"s ;

// 状態の判定をする関数
auto status = []( double bmi )
Expand Down Expand Up @@ -93,7 +93,7 @@ int main()
double bmi = mass / (height*height) ;

// BMIの出力
std::cout << "BMI=" << bmi << "\n"s ;
std::cout << "BMI="s << bmi << "\n"s ;
}
~~~

Expand Down Expand Up @@ -335,7 +335,7 @@ $ cat bodymass.txt
1.63
73
$ ./bmi < bodymass.txt > index.txt
$ cat index.text
$ cat index.txt
27.4756
~~~

Expand Down
2 changes: 1 addition & 1 deletion 009-vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ for ( std::size_t head = 0 ; head != size ; ++head )
// 現在の先頭の次の要素から探すのでhead + 1
for ( std::size_t index = head + 1 ; index != size ; ++index )
{
if ( v.at(index) < v.at(min)
if ( v.at(index) < v.at(min) )
min = index ;
}

Expand Down
5 changes: 3 additions & 2 deletions 011-integer.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ int main()

また、ビットという単位も扱いづらい。コンピューターは膨大な情報を扱うので、ビットをいくつかまとめたバイト(byte)を単位として情報を扱っている。1バイトが何ビットであるかは環境により異なる。本書では最も普及している1バイトは8ビットを前提にする。

1ビットは2種類の状態を表現できるので、1バイトの中の8ビットは$2^8 = 256$種類の状態を表現できる。2バイトならば16ビットとなり、$2^16 = 65536$種類の状態を表現できる。
1ビットは2種類の状態を表現できるので、1バイトの中の8ビットは$2^8 = 256$種類の状態を表現できる。2バイトならば16ビットとなり、$2^{16} = 65536$種類の状態を表現できる。

### 1バイトで表現された整数

Expand All @@ -140,12 +140,13 @@ auto max = 0b11111111 ;

正数だけを表現するならば話は簡単だ。1バイトの整数は0から255までの値を表現できる。これを符号なし整数(unsigned integer)という。


では負数を表現するにはどうしたらいいだろう。正数と負数を両方扱える整数表現のことを、符号付き正数(signed integer)という。1バイトは256種類の状態しか表現できないので、もし$-1$を表現したい場合、$-1$から254までの値を扱えることになる。

$-1$しか扱えないのでは実用的ではないので、負数と正数を同じ種類ぐらい表現したい。256の半分は128だが、1バイトで表現された整数は$-128$から128までを表現することはできない。0があるからだ。0を含めると、1バイトの整数は最大で$-128$から127までか、$-127$から128までを表現できる。どちらかに偏ってしまう。


では実際に1バイトで負数も表現できる正数表現を考えてみよう
では実際に1バイトで負数も表現できる整数表現を考えてみよう

#### 符号ビット

Expand Down
4 changes: 2 additions & 2 deletions 013-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ int f() { return 1 ; }

int main()
{
using namespace std ;
using namespace abc ;

// エラー、名前が曖昧
f() ;
Expand All @@ -523,7 +523,7 @@ int f() { return 1 ; }

int main()
{
using namespace std ;
using namespace abc ;

// OK、名前空間abcのf
abc::f() ;
Expand Down
2 changes: 1 addition & 1 deletion 014-iterator.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ c) 2 \le i \le 12
$$

$$
d) 2 \lt i \lt 13
d) 1 \lt i \lt 13
$$

C++のイテレーターはa)を元にしている。
Expand Down
5 changes: 3 additions & 2 deletions 016-algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ int main()
}
~~~

関数`printf_all`は便利だが、重要な処理がハードコードされている。例えば要素の集合のうち100以下の値だけ出力したいとか、値を2倍して出力したいとか、値を出力するたびに改行を出力したいという場合、それぞれに関数を書く必要がある。
関数`print_all`は便利だが、重要な処理がハードコードされている。例えば要素の集合のうち100以下の値だけ出力したいとか、値を2倍して出力したいとか、値を出力するたびに改行を出力したいという場合、それぞれに関数を書く必要がある。

~~~cpp
// 値が100以下なら出力
Expand Down Expand Up @@ -969,6 +969,7 @@ int main()
}
~~~


`result`に代入されるのは関数`op`の戻り値だ。関数`op`は値を1つの引数受け取り値を返す関数だ。

## replace
Expand Down Expand Up @@ -1150,7 +1151,7 @@ int main()
}
~~~

`remove_if(first, last, pred)`は、`[first, last]`の範囲の要素を指すイテレーター`i`のうち、関数`pred`に渡した結果`pred(*i)``true`になる要素を取り除くアルゴリズムだ。
`remove_if(first, last, pred)`は、`[first, last)`の範囲の要素を指すイテレーター`i`のうち、関数`pred`に渡した結果`pred(*i)``true`になる要素を取り除くアルゴリズムだ。

~~~cpp
int main()
Expand Down
2 changes: 1 addition & 1 deletion 025-array-iterator.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace std
{ return c.end() ;}

template < typename C >
auto begin( C const & c )
auto end( C const & c )
{ return c.end() ;}
}
~~~
Expand Down
4 changes: 2 additions & 2 deletions 028-pointer-semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ int main()
int x = 0 ;

// rはxを参照する
int r = x ;
int & r = x ;

int y = 1 ;

Expand Down Expand Up @@ -216,7 +216,7 @@ int * pointer = nullptr ;
double * p1 = nullptr ;

// std::stringへのポインター
std::string p2 = nullptr ;
std::string * p2 = nullptr ;
~~~

C言語とC++では歴史的な理由で、`nullptr`のほかにも`NULL`もnullポインター値
Expand Down
2 changes: 1 addition & 1 deletion 029-pointer-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ using pointer_to_array_type = int (*)[5] ;
int main()
{
int a[5] ;
pointer_to_function_type = &a ;
pointer_to_array_type ptr = &a ;
}
~~~

Expand Down
2 changes: 1 addition & 1 deletion 030-pointer-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ int main()
140722117900228
~~~
最初の値が`a0`, 次の値が`a3`, 最後の値が`a3`だ。
最初の値が`a0`, 次の値が`a3`, 最後の値が`a1`だ。
筆者の環境では`sizeof(int)`は`4`だ。すると`a3`の値は`a0`の値より12多い値になっているはずだ。実際にそうなっている。`a1`は`a3`に対して8少ない値になっているはずだ。実際にそうなっている。
Expand Down
2 changes: 1 addition & 1 deletion 033-vector-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ void destroy_at( T * location )
destroy_at( s ) ;
~~~
このようなコードを書くのは面倒なので、標準ライブラリには`std::destory_at`がある。また、これらをひっくるめたアロケーターを使うためのライブラリである`allocator_traits`がある。
このようなコードを書くのは面倒なので、標準ライブラリには`std::destroy_at`がある。また、これらをひっくるめたアロケーターを使うためのライブラリである`allocator_traits`がある。
## `std::allocator_traits<Alloc>`
Expand Down
66 changes: 66 additions & 0 deletions 034-vector-memory-allocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -836,3 +836,69 @@ void shrink_to_fit()

この実装は`reserve`と似ている。

# vectorのその他のコンストラクター

## イテレーターのペア

`std::vector`はイテレーターのペアを取り、その参照する値で要素を初期化できる。

~~~cpp
int main()
{
std::array<int, 5> a {1,2,3,4,5} ;
std::vector<int> v( std::begin(a), std::end(a) ) ;
// vは{1,2,3,4,5}
}
~~~

これはすでに実装したメンバー関数を使えば簡単に実装できる。

~~~c++
template < typename InputIterator >
vector( InputIterator first, InputIterator last, const Allocator & = Allocator() )
{
reserve( std::distance( first, last ) ;
for ( auto i = first ; i != last ; ++i )
{
push_back( *i ) ;
}
}
~~~
## 初期化リスト
`std::vector`は配列のように初期化できる。
~~~cpp
int main()
{
std::vector<int> v = {1,2,3} ;
}
~~~

このような初期化を*リスト初期化*と呼ぶ。

リスト初期化に対応するためには、`std::initializer_list<T>`を引数に取るコンストラクターを追加する。

~~~c++
template < typename T, Allocator = std::allocator<T> >
{
// コンストラクター
vector( std::initializer_list<value_type> init, const Allocator & = Allocator() ) ;
// 省略...
} ;
~~~

`std::initializer_list<T>``T`型の要素を格納する標準ライブラリで、`{a,b,c,...}`のようなリスト初期化で構築することができる。

~~~c++
std::initializer_list<int> init = {1,2,3,4,5} ;
~~~
`std::initializer_list`は`begin/end`によるイテレーターを提供しているので、すでに実装したコンストラクターにデリゲートすればよい。
~~~c++
vector( std::initializer_list<value_type> init, const Allocator & alloc = Allocator() ) ;
: vector( std::begin(init), std::end(init), alloc )
{ }
~~~
2 changes: 1 addition & 1 deletion 036-move.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,4 @@ C++ではコピーはコピー元を変更しないという慣習がある。
このため、C++はコピーのほかにムーブを定めている。ムーブを使うにはムーブ元の変数`x``std::move(x)`のようにしてコピーする。`std::move`はこのコピーはコピーではなくムーブしてもよいというヒントになる。


ムーブを実装するためには、まず基礎知識として次の章で学ぶr`value`リファレンス、値カテゴリー、テンプレートのフォワードリファレンスの深い理解が必要になる。
ムーブを実装するためには、まず基礎知識として次の章で学ぶ`rvalue`リファレンス、値カテゴリー、テンプレートのフォワードリファレンスの深い理解が必要になる。
16 changes: 8 additions & 8 deletions 041-move-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,19 @@ Integer & operator -=( const Integer & r )

複合代入演算子をムーブ代入演算子として実装する理由は、通常はない。

## 単行演算子
## 単項演算子

演算を表現するクラスでオーバーロードしたい単行演算子には`operator +``operator -`がある。特に`operator -`は実用上の意味があるので実装してみよう。
演算を表現するクラスでオーバーロードしたい単項演算子には`operator +``operator -`がある。特に`operator -`は実用上の意味があるので実装してみよう。

~~~cpp
Integer a(10) ;
auto b = -a ;
// これは二項演算子 operator +の結果に
// 単行演算子operator -を適用
// 単項演算子operator -を適用
auto c = -(a + a) ;
~~~
`*this`が`lvalue`の場合の単行演算子の実装は以下のようになる
`*this`が`lvalue`の場合の単項演算子の実装は以下のようになる
~~~cpp
Integer operator -() const
Expand Down Expand Up @@ -200,9 +200,9 @@ c += a ;
c.make_it_negative() ;
~~~

こんなコードを書くのは面倒だ。単に`-(a+a)`と書いて効率的に動いてほしい。そのために単行演算子`operator -`をムーブに対応させる。
こんなコードを書くのは面倒だ。単に`-(a+a)`と書いて効率的に動いてほしい。そのために単項演算子`operator -`をムーブに対応させる。

単行演算子はクラスのメンバー関数として実装する
単項演算子はクラスのメンバー関数として実装する

~~~cpp
class Integer
Expand Down Expand Up @@ -357,7 +357,7 @@ struct X
struct X
{
// エラー、リファレンス修飾子がない
void f()
void f() ;
void f() & ;

// OK、リファレンス修飾子がある
Expand Down Expand Up @@ -393,7 +393,7 @@ public :
} ;
~~~

`rvalue`リファレンス修飾子を使った単行演算子`operator -`の実装は、`*this`自身が`rvalue`であるので、自分自身をムーブしている。ムーブ以降、`this->ptr``nullptr`になる。なぜならば、`Integer`のムーブ代入演算子がそのような実装になっているからだ。
`rvalue`リファレンス修飾子を使った単項演算子`operator -`の実装は、`*this`自身が`rvalue`であるので、自分自身をムーブしている。ムーブ以降、`this->ptr``nullptr`になる。なぜならば、`Integer`のムーブ代入演算子がそのような実装になっているからだ。

~~~cpp
/// 上で示したのと同じムーブ代入演算子の抜粋
Expand Down
Loading

0 comments on commit 3f6c45a

Please sign in to comment.