2014年7月17日木曜日

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

今回はItem 1の残り、配列型と関数ポインタ型の型推定です。
配列型は、自分でも関数に渡すとポインタ型に退化してしまって要素数が取れなくなるのを不便に感じたことがあるのですが、テンプレートで参照渡しにするとその問題が解決するのを不思議に思っていましたが、今回の説明を読んでそれが仕様通りの現象であることを理解できました。

配列引数

配列型とポインタ型は同一視されがちだが、実は違う
配列のポインタ型は、配列型が配列の先頭要素へのポインタに壊変したもの

const char name[] = "J. P. Briggs"; // nameの型はconst char[13]
const char * ptrToName = name; 配列型はポインタ型に壊変した

しかし、パラメータ型は強制的にポインタ型に壊変してしまう
void myFunc(int param[]);
これは以下の宣言と同じになってしまう
void myFunc(int* param);

これはC++がC言語を元としているため生じたもの。
下記のように値渡しのテンプレート関数では、配列を渡すとポインタ型になる。
template <typename T>
void f(T param); // 値渡しのテンプレート関数

f(name); // nameはconst char[13]型だが、const char *型と型推定される
しかし、参照渡しのテンプレート関数では、配列の参照型にできる

template <typename T>
void f(T& param); // paramはconst char (&)[13]型となる

f(name); // nameはconst char [13]型
これを使うと、コンパイル時定数として配列の要素数を返すconstexpr関数を定義できる
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) {
    return N;
}

int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; 

int mappedVals[arraySize(keyVals)]; 

関数引数

配列型と同じように、関数型も関数ポインタ型に壊変する

void someFunc(int, double); // 型はvoid(int, double)

template<typename T>
void f1(T param); // 値渡し

template<typename T>
void f2(T& param); // 参照渡し

f1(someFunc); // 値渡しは関数ポインタ型void (*)(int, double)に。

f2(someFunc); // 参照渡しは関数の参照型void (&)(int, double)に。

Things to Remember

  • ポインタ型とUniversa Referenceでない型のパラメータでは、引数が参照型かどうかは無視される
  • パラメータがUniversal Referenceの場合は、lvalueはlvalue referenceに、rvalueはrvalue referenceになる
  • パラメータが値渡しの場合、引数が参照型かどうか、constかどうかは無視される
  • 配列型と関数型の引数は、パラメータが参照型でない限り、ポインタ型に壊変する

0 件のコメント:

コメントを投稿