2014年7月20日日曜日

【読書ノート】Effective Modern C++ - Item 3 [1 / 2]

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 3はdecltypeについてです。
自分はC++11の関数の戻り値を後ろに書く記法とC++14のdecltype(auto)をちゃんと知らなかったので、学ぶところが多かったです。

Item 3: decltypeを理解しよう

decltypeは名前や式の型を教えてくれる。
典型的の場合は予想通りの動きをしてくれるが、時には予想に反する動きをする。

典型的な場合

const int i = 0; // decltype(i)はconst int
bool f(const Widget& w); // decltype(w)はconst Widget&
    // decltype(f)はbool(const Widget&)

struct Point {
    int x, y; // decltype(Point::x)はint。Point::yも同様。
};

Widget w; // decltype(w)はWidget

if (f(w)) ... // decltype(f(w))はbool

template<typename T>
class vector {
public:
    ...
    T& operator[](std::size_t index);
    ...
};

vector v; // decltype(v)はvector
...
if (v[0] == 0) ... // decltype(v[i])はint&

関数の戻り値の推定

C++11の場合

主なdecltypeの使用場所はtemplate関数で、テンプレートパラメータに依存する型を返したい時だ。

template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) // 改善の余地あり(後述)
    -> decltype(c[i])
{
    authenticateUser();
    return c[i];
}

これにより、c[i]が何の型を返そうともそれをそのまま関数の戻り値とすることができる。
なお、この関数名の前のautoは型推定とは関係ない。
このautoは単にC++11のtrailing return type syntax(戻り値を後置する記法)が使用されることを示す。
この後置のメリットは、戻り値の型の計算に関数パラメータを使用できること。
(上記例ではcとi。後置を使わないと、cとiは利用できない。)

C++14の場合

C++14では、以下のようにautoだけで戻り値の型推定が行われるようになった。
(この場合はもちろん、autoは型推定を示すキーワードとなる。)

template<typename Container, typename Index> // C++14の記法 + 改善の余地あり
auto authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i]; // 戻り値の型がc[i]から推定される
}

しかしこの場合、型推定のルールは?autoとdecltypeのどちらかのもの?
驚くかもしれないが、答えはテンプレートの型推定のルールになる。
ただ、初期化リスト{}を除けばautoとテンプレートの型推定のルールは同じだ。
しかし今回の場合はこのルールだと問題になる。
operator[]は大抵参照を返すが、テンプレートの型推定では参照を取り除いてしまうからだ。
従って、以下のコードはコンパイルエラーになる。

std::deque d;
...
authAndAccess(d, 5) = 10; // コンパイルエラー

これは、d[5]はint&を返すがテンプレートの型推定によって戻り値の型はintになるため。
すると、rvalueに10を代入しようとしていることになるため、コンパイルエラーとなる。
この問題は、decltype(auto)指定子によってdecltypeの型推定をさせるようにすれば解決する。

template<typename Container, typename Index> // C++14の記法 + 改善の余地あり
decltype(auto)
authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i];
}

変数宣言にdecltype

decltype(auto)は変数宣言にも有効な場面がある。

Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // myWidget1の型はWidget
decltype(auto) myWidget2 = cw; // myWidget2の型はconst Widget&

authAndAccessで改善の余地があるとされていることと、decltypeの型推定の典型的でない場合については、次回の記事で。

0 件のコメント:

コメントを投稿