C++のつまずきポイント解説 その1

#C++のつまずきポイントを無駄に詳説
はじめまして（？）、いなむ先生と申します
C++アドベントカレンダー12日目は
オンラインコミュニティ「C++の会」での質問や
読書会の会話や
自分の体験をもとに
C++のつまずきポイントを解説します（会話の焼きましです
Template大好きな人なのでTemplate関連が多い（すべて？

##std::arrayのuniform initialization

C++11からはuniform initializationがサポートされていますが
`std::array`はコンストラクタが定義されておらず
組み込み配列と同様に初期化リストで初期化する値を指定できます
ではさっそく、

```cpp
std::array< int, 4 > a{ 1, 2, 3, 4 } ; // error!
```

残念ながらエラーです
std::arrayはメンバ変数として配列を持つため初期化子が配列を初期化する必要があります
配列の初期化はリスト初期化です
つまり、二重に波カッコを書く必要があります
もしくは初期化代入にするかのどちらかです

```cpp
std::array< int, 4 > a{ { 1, 2, 3, 4 } } ; // OK!
std::array< int, 4 > b = { 1, 2, 3, 4 } ; // OK!
```
OKなのですが下のコードはclangだとwarningになり波カッコを二重にするよう促されるようです

##autoの注意点
`auto`は非常に便利ですが、幾つか知っておくべきことがあります

###autoの一意性
一つの`auto`は一つの型を表していなければなりません
例えば、次のようなコード

```cpp
auto a=1,b=1.0; // error! aとbは同じ型に推論されなければならない
```

`auto`は一意でなければならないので`a`が`int`, `b`が`double`に推測されるこのコードはill-formedになります

###autoとtemplateの違い
`auto`は`{}`で初期化した場合には必ず`std::initializer_list`であると推測します
対してテンプレートパラメータ`T`は`std::initializer_list`とは推測しません

```cpp
template < typename T>
void f( T ) {} ;

auto x = { 1, 3, 5 } ; // x's type is std::initializer_list<int>

f( { 1, 3, 5 } ) ; // can't deduce T
```

この関数の推測は、引数を未知の型`T`の`initializer_list`とすることで解決できる

```cpp
template < typename T>
void f( std::initializer_list<T> ) {} ;

f( { 1, 3, 5 } ) ; // OK! T is int, and parameter type is initializer_list<int>
```

C++14からは、関数の戻り値を`auto`にすることもできる
ここで注意すべきなのは戻り値を`auto`にした関数の戻り値の推論は
autoの規則ではなく、__templateの規則で行われる__ということだ
つまり、戻り値に記述された`braced-init list( {}で囲まれたリスト )`が`initializer_list`と推論されない

```cpp
auto f()
{
    return { 1, 3, 5 } ; // can't deduce !
}
```


##コンテナの選び方
「コンテナの種類が多くて、どれを使えばいいのかわからない」
なんてことはあるかもしれません

たくさんの種類がありますが
最有力候補は`vector`です
単純なゆえにほとんどの場合に高速です

固定長の配列を用いたいいたい場合は配列型（`int[5]`のような）を用いるよりも高機能な`array`を用いるのがよいです

値の高速な検索を必要とする場合は`map`や`unordered_map`のような連想コンテナを使用するとよいでしょう
mapと名のついたコンテナは固有のメンバ関数`find()`をもち、内部構造を利用した高速な検索を提供します

以下に、標準ライブラリで提供されるコンテナの種類と特徴をまとめました

| コンテナの名前  | コンテナの説明 | カテゴリ |
|:------------|:------------|:-----------|
| vector | 動的配列 | シーケンスコンテナ |
| array | 固定長配列 | シーケンスコンテナ |
| list | 双方向リスト. 任意位置要素の挿入削除が高速 | シーケンスコンテナ |
| forward_list | 単方向リスト | シーケンスコンテナ |
| deque | 両端キュー | シーケンスコンテナ |
| queue | キュー | コンテナアダプタ |
| priority_queue | 優先順序付きキュー | コンテナアダプタ |
| stack | スタック | コンテナアダプタ |
| map | 連想配列. キーと値のペアを保持する. キーから値を取り出すのが O(log N) と高速 | 連想コンテナ |
| multimap | 重複可能な連想配列 | 連想コンテナ |
| unordered_map | ハッシュによる連想配列 | 非順序連想コンテナ |
| unordered_multimap | ハッシュによる重複可能な連想配列 | 非順序連想コンテナ |
| set | 順序付集合 | 連想コンテナ |
| multiset | 重複可能な集合（多重集合） | 連想コンテナ |
| unordered_set | ハッシュによる集合 | 非順序連想コンテナ |
| unordered_multiset | ハッシュによる重複可能な集合（多重集合） | 非順序連想コンテナ |

<dl>
  <dt>シーケンスコンテナ</dt>
  <dd>要素の順序が維持されるコンテナ</dd>
  <dt>連想コンテナ</dt>
  <dd>要素が整列されて格納されるコンテナ</dd>
  <dt>非順序連想コンテナ</dt>
  <dd>要素に順序がなく、ハッシュを用いて管理されるコンテナ</dd>
  <dt>コンテナアダプタ</dt>
  <dd>実際にはコンテナではなく、他のコンテナへの操作を一部制限する形でデータ構造を表現するコンテナ</dd>
</dl>

##コンテナとイテレータ
イテレータとはコンテナとアルゴリズムを仲介するインターフェイスです
1. `operator*`で値を取り出せる
2. `operator++`で次の要素をさすイテレータに進めることができる
を満たすものをイテレータとして扱うことができます

標準ライブラリのアルゴリズムは具体的なコンテナオブジェクトを操作するのではなく
イテレータにより横断可能な範囲を操作の対象とします
このことによりあらゆるコンテナに対してアルゴリズムが適用できるようになっています

コンテナには異なる種類のイテレータを取得できるメンバ関数（フリー関数もある）があるので目的に合わせて使い分けましょう
`begin(), end()`   : ふつうのiterator
要素を変更するときにどうぞ

```cpp
std::vector<int> v{1,2,3,4,5} ;

for ( auto&& iter = v.begin(); iter != v.end(); ++iter)
    *iter = 0;
```

`cbegin(), cend()` : const_ieratorを取得
constイテレータ、要素を変更しないときにどうぞ

```cpp
std::vector<int> v{1,2,3,4,5} ;

for ( auto&& iter = v.cbegin(); iter != v.cend(); ++iter)
    std::cout << *iter << std::endl;
```

`rbegin(), rend()` : reverse_iteratorを取得
逆順イテレータ、ケツから走査して要素を変更したいときにどうぞ

```cpp
    std::vector<int> v1{1,2,3,4,5,6,7,8,9};
    std::vector<int> v2{1,2,3,4,5};
    
    for(auto&& iter1 = v1.rbegin(), iter2 = v2.rbegin(); iter2 != v2.rend(); ++iter1,++iter2)
        *iter2 = *iter1;
```

`crbegin(), crend()` : const_reverse_iteratorを取得
const逆順イテレータ、ケツから走査して要素を変更しないときにどうぞ

```cpp
std::vector<int> v{1,2,3,4,5} ;

for ( auto&& iter = v.crbegin(); iter != v.crend(); ++iter)
    std::cout << *iter << std::endl;
```

イテレータには幾つか種類があり、できることが違います

###イテレータの分類
標準ライブラリで定義されるイテレータの分類
標準ライブラリが提供するアルゴリズムは次のいずれかのイテレータを引数として要求する

<dl>
  <dt>入力イテレータ（Input Iterator）</dt>
  <dd>イテレータの前進、要素の読み出しができる（std::istream_iterator）</dd>
  <dt>出力イテレータ（Output Iterator）</dt>
  <dd>イテレータの前進、要素の書き込みができる（std::ostream_iterator, insert_iterator）</dd>
  <dt>前方イテレータ（Forward Iterator）</dt>
  <dd>イテレータの前進、要素の読み出し、書き込みができる<br>
前進イテレータは入力イテレータ、出力イテレータが要求される場合にも使用できる<br>
（forward_list, unordered_map, unordered_setにおけるイテレータなどが該当）</dd>
  <dt>双方向イテレータ（Bidirectional Iterator）</dt>
  <dd>イテレータの前進と後退、要素の読み出し、書き込みができる<br>
双方向イテレータは前進イテレータが要求される場合にも使用できる<br>
（list, map, setにおけるイテレータなどが該当）</dd>
  <dt>ランダムアクセスイテレータ（Random Access Iterator）</dt>
  <dd>イテレータの前進と後退、要素の書き込み、読み出し加えて、任意の位置にある要素にアクセスできる<br>
ランダムアクセスイテレータは双方向イテレータが要求される場合でも使用できる<br>
（array, deque, vectorにおけるイテレータなどが該当）<br>
配列の要素をさすポインタもランダムアクセスイテレータとして扱うことができる</dd>
</dl>




###iteratorとconst_iteratorとconst interator

以下のコードがすべてを語ってくれる

```cpp

    std::vector<int> v(4);
    // normal iterator
    std::vector<int>::iterator iter = v.begin();
    // const_iterator
    const std::vector<int>::iterator c_iter = v.begin();
    // const interator
    std::vector<int>::const_iterator const_iter = v.begin();
    
    ++iter;         // OK
    ++const_iter;   // OK
    ++c_iter;       // c_iterはconstなので変更できない
    
    *iter=3;        // OK
    *c_iter=3;       // c_iterはconstだが, *c_iterはconstではないのでOK
    *const_iter=3;  // これはエラー

```


はっきり言って、インクリメントできない`const iterator`の出番はほぼない

また、当たり前だがconstな要素を持つコンテナのイテレータは`const_iterator`となる点には理解が必要

```cpp
    const std::vector<int> v(4);

    auto&& const_iter = v.begin();
    
    ++const_iter ;
    *const_iter=3;  // これはエラー
```
##ラムダ式の推測
関数の中でラムダ式の戻り値や引数の型を推測したいなんてことがあるかもしれません
型推論は`decltype`を用いて

```cpp
decltype( x + y )
```

のように書くとことができますが
ラムダ式を`decltype`の中に記述することはできません
また、ラムダ式はそれぞれユニークな型を持ちます
そこで、関数ポインタに変換すると型推論できます

```cpp
#include <vector>
#include <iostream>
#include <type_traits>
#include <boost/type_index.hpp>

template<typename R, typename F, typename A>
auto return_helper(R(F::*)(A) const) -> R;

template<typename R, typename F, typename A>
auto arg_helper(R(F::*)(A) const) -> A;

template < typename F >
void func(F){
    using Return_t = decltype(return_helper(&F::operator()));
    using Arg_t = decltype(arg_helper(&F::operator()));
    std::cout << "return type : " << boost::typeindex::type_id_with_cvr<Return_t>().pretty_name() << std::endl;
    std::cout << "arg type    : " << boost::typeindex::type_id_with_cvr<Arg_t>().pretty_name() << std::endl;
}

int main()
{
    func([](double&&){return std::vector<int>{1,2,3};});
    return 0;
}
```

実行結果
>return type : std::vector\<int, std::allocator\<int\> \>
arg type    : double&&

一応推論できています

##テンプレート型推論規則
テンプレートはC++に欠かせない機能です
テンプレートの型推論規則を知らないとよくつまずきます
テンプレート型推論規則では`T&&`や`auto&&`はユニヴァーサル参照と呼ばれ特別扱いされます

「Effective Modern C++」の第1章にそって 
関数のパラメータの宣言に分けて解説します

###テンプレート型推論その１（パラメータタイプが参照もしくはポインタ型）

```cpp

template < typename T >
void func( T& ) ;

int x = 2 ;         // x は int
const int cx= x ;   // cx は const int
const int& rx = x ; // rx は x への const int 参照

func(x) ;     // T は int, パラメータタイプは int&
func(cx) ;    // T は const int, パラメータタイプは const int&
func(rx) ;    // T は const int, パラメータタイプは const int&

```

`T&`を仮引数にとる関数にconst修飾された型を渡すとconstは型の一部として扱われます
参照は読み飛ばされます

###テンプレート型推論その２（パラメータタイプがユニヴァーサル参照）

ユニヴァーサル参照を仮引数にとる場合は
左辺値が渡されたときには`T`は左辺値参照であると推論されます
右辺値が渡されたときはその１の規則が適用されます

```cpp

template < typename T >
void func( T&& ) ;

int x = 2 ;         // x は int
const int cx= x ;   // cx は const int
const int& rx = x ; // rx は x への const int 参照

func(x) ;     // xは左辺値, よって T は int&, パラメータタイプは int&
func(cx) ;    // cxは左辺値, よって T は const int&, パラメータタイプは const int&
func(rx) ;    // rxは左辺値, よって T は const int&, パラメータタイプは const int&
func(2) ;     // 2は右辺値, よって T は int, パラメータタイプは int&&
```

特別な規則で右辺値と左辺値が区別されることを知っておかなくてはなりません

###テンプレート型推論その３（パラメータタイプが参照でもポインタでもない）

仮引数が参照でもポインタでもない場合は値渡しになり、その場合の規則は
1. cv修飾は無視される
2. 参照は無視される
となります

```cpp

template < typename T >
void func( T ) ;

int x = 2 ;         // x は int
const int cx= x ;   // cx は const int
const int& rx = x ; // rx は x への const int 参照

func(x) ;     // T は int, パラメータタイプは int
func(cx) ;    // T は int, パラメータタイプは int
func(rx) ;    // T は int, パラメータタイプは int
```

###テンプレート型推論：蛇足（仮引数がcv修飾されている場合）
N4140の14.8.2.1 Deducing template arguments from a function callを参照すると

>3 If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a
reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cvunqualified
template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in
place of A for type deduction. [ Example:

>```cpp
template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue
```
>—end example ]

要約すると、仮引数がcv修飾されている場合には推論の段階で引数の型のcv修飾は無視されます
その後、参照も無視されて型推論されます

```cpp
template < typename T >
void f( T& ) ;

template < typename T>
void g( const T& ) ;

int x = 2 ;         // x は int
const int cx= x ;   // cx は const int
const int& rx = x ; // rx は x への const int 参照

f(x) ;     // T は int, パラメータタイプは int&
f(cx) ;    // T は const int, パラメータタイプは const int&
f(rx) ;    // T は const int, パラメータタイプは const int&

g(x) ;     // T は int, パラメータタイプは const int&
g(cx) ;    // T は int, パラメータタイプは const int&
g(rx) ;    // T は int, パラメータタイプは const int&

```

となります
`T&`を使うとconst性が伝播します
`const T&`を使うとconst性を強制できます
また、const な lvalue reference はrvalueを束縛できるので右辺値、左辺値についてconst lvalue参照できます

以上がテンプレートの型推論規則です


###明示的な型指定
明示的に型を指定した場合はどうでしょう
明示的に型をわたして関数テンプレートを使用するということは
型推論とは別の規則が適用されるということです
以下のコードはどのように扱われるのでしょうか？

```cpp
template < typename T >
void f( T&& ) ;

int x = 2 ;

f<const int>(x) ;
```

結論からいうとこのコードはコンパイルエラーになります
順をおって説明すると
明示的に型を指定すると指定された型がそのまま`T`に適用されます

つまり、Tは`const int`であり
パラメータタイプは`const int&&`となります
右辺値参照は左辺値を束縛できないのでエラーになります

この例では直感的には`T`が`const int`になり, パラメータタイプをユニヴァーサル参照の規則で`const int&`
とすることでconst性を強制しようとしていますが、失敗します

ユニヴァーサル参照の最大の利点は__ムーブ・セマンティクス__が使える点でしょう
const性を強制するとムーブ・セマンティクスが使えないため`const T&`を使うべきです

更に危険なコードを書くことができます

```cpp
template < typename T >
void f( const T& x ){
    x = 0 ;
}

int x = 2 ;
f< int& >( x ) ;
```

このコードはconst指定されているはずの変数`x`を書きかえています
コンパイルエラーになってほしいです
が！
残念なことに（？）コンパイルでき、変数を書きかえることに成功します！
参照型を明示的に関数テンプレートにわたしたときには
パラメータタイプレベルでのcv修飾は無視されれるのです

まあ、こんな意味不明な使い方をする人はいないかもしれません


##跋文
この記事を書いている間にも、どんどん躓いたため
非常に長い記事になってしまいました
最後まで読んでくださった方はありがとうございます
おかしな箇所があれば、ご指摘ください
明日は、okdshinさんの記事です。

##参考文献
『新改訂版 C++ポケットリファレンス』
(高橋晶、安藤敏彦、一戸優介、楠田直矢、道化師、湯朝剛介　著、株式会社技術評論社、ISBN 978-4-7741-7408-2)

『Effective Modern C++ ―C++11/14プログラムを進化させる42項目』
(Scott Meyers 著、オライリー・ジャパン、ISBN 978-4-87311-736-2)

[N3777](https://github.com/cplusplus/draft/tree/master/papers)
[N4140](https://github.com/cplusplus/draft/tree/master/papers)