Skip to content

Commit

Permalink
add const
Browse files Browse the repository at this point in the history
  • Loading branch information
EzoeRyou committed Jun 28, 2018
1 parent aa58404 commit d52d9db
Showing 1 changed file with 168 additions and 6 deletions.
174 changes: 168 additions & 6 deletions 015-reference.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# ポップカルチャーリファレンス
# lvalueリファレンスとconst

ポップカルチャーリファレンスというのは流行の要素をさり気なく作品中に取り入れることで、流行作品を知っている読者の笑いを誘う手法である
-- キャプテン・オブビウス ポップカルチャーリファレンスについて

## lvalueリファレンス

変数に変数を代入すると、代入元の値が代入先にコピーされる。代入先の値を変更しても、コピーされた値が変わるだけで、代入元には一切影響がない。

~~~cpp
Expand Down Expand Up @@ -37,7 +39,7 @@ int main()
}
~~~
しかし、時には変数の値を直接書き換えたい場合がある。この時リファレンス(reference)が使える。リファレンスは変数に`&`を付けて宣言する
しかし、時には変数の値を直接書き換えたい場合がある。この時lvalueリファレンス(reference)が使える。lvalueリファレンスは変数に`&`を付けて宣言する
~~~cpp
int main()
Expand All @@ -52,9 +54,19 @@ int main()
}
~~~

この例で、変数refは変数aへのリファレンスなので、変数aと同じように使える。
この例で、変数refは変数aへの参照(リファレンス)なので、変数aと同じように使える。

lvalueリファレンスは必ず初期化しなければならない。

~~~c++
int main()
{
// エラー
int & ref ;
}
~~~

リファレンスは関数でも使える
lvalueリファレンスは関数でも使える

~~~cpp
void f( int & x )
Expand Down Expand Up @@ -108,11 +120,11 @@ auto swap = []( auto a, auto b )

この実装では、変数は単にコピーされるだけなので、関数の呼び出し元には何の影響もない。

これをリファレンスに変えると、関数の呼び出し元の変数の値を交換する関数`swap`が作れる。
これをlvalueリファレンスに変えると、関数の呼び出し元の変数の値を交換する関数`swap`が作れる。


~~~cpp
// リファレンス
// lvalueリファレンス
auto swap = []( auto & a, auto & b )
{
auto temp = a ;
Expand All @@ -135,3 +147,153 @@ int main()
// b == 1
}
~~~

ところで、この章では一貫してlvalueリファレンスと書いているのに気がついただろうか。lvalueとは何なのか、lvalueではないリファレンスはあるのか。その疑問は後の章で解決する。

## const

値を変更したくない変数は、constをつけることで変更を禁止できる。

~~~c++
int main()
{
int x = 0 ;
x = 1 ; // OK、変更できる

const int y = 0 ;
y = 0 ; // エラー、変更できない。
}
~~~

constはちょっと文法が変わっていて混乱する。例えば、`const int`でも`int const`でも意味が同じだ。

~~~cpp
int main()
{
// 意味は同じ
const int x = 0 ;
int const y = 0 ;
}
~~~

constはlvalueリファレンスと組み合わせることができる。


~~~c++
int main()
{
int x = 0 ;

int & ref = x ;
// OK
++ref ;

const int & const_ref = x ;

// エラー
++const_ref ;
}
~~~

constは本当に文法が変わっていて混乱する。`const int &``int const &`は同じ意味だが、`int & const`はエラーになる。

~~~c++
int main()
{
int a = 0 ;

// OK、意味は同じ
const int & b = a ;
int const & c = a ;

// エラー
int & const d = a ;
}
~~~

これはとても複雑なルールで決まっているので、こういうものだと諦めて覚えるしかない。


constがついていない型のオブジェクトをconstなlvalueリファレンスで参照することができる。

~~~cpp
int main()
{
// constのついていない型のオブジェクト
int x = 0 ;

// OK
int & ref = x ;
// OK、constはつけてもよい
const int & cref = x ;
}
~~~

constのついている型のオブジェクトをconstのついていないlvalueリファレンスで参照することはできない。

~~~c++
int main()
{
// constのついている型のオブジェクト
const int x = 0 ;

// エラー、constがない。
int & ref = x ;

// OK、constがついている
const int & cref = x ;
}
~~~

constのついているlvalueリファレンスは何の役に立つのかというと、関数の引数を摂るときに役に立つ。

例えば以下のコードは非効率的だ。

~~~cpp
void f( std::vector<int> v )
{
std::cout << v.at(1234) ;
}

int main()
{
// 10000個の要素を持つvector
std::vector<int> v(10000) ;

f( v ) ;
}
~~~
なぜかというと、関数の引数に渡すときに、変数`v`はコピーされるからだ。
リファレンスを使うと不要なコピーをしなくて済む。
~~~cpp
void f( std::vector<int> & v )
{
std::cout << v.at(1234) ;
}
~~~

しかし、リファレンスで受け取ると、うっかり変数を変更してしまった場合、その変更が関数の呼び出し元に反映されてしまう。

~~~cpp
// 値を変更するかもしれない
void f( std::vector<int> & v ) ;

int main()
{
// 要素数10000のvector
std::vector<int> v(10000) ;

f(v) ;

// 値は変更されているかもしれない
}
~~~
このとき、constなlvalueリファレンスを使うと、引数に取った値を変更しないことを保証できる。
~~~cpp
void f( std::vector<int> const & v ) ;
~~~

0 comments on commit d52d9db

Please sign in to comment.