Contenu connexe Similaire à すごいConstたのしく使おう! (15) すごいConstたのしく使おう!2. はじめに
自己紹介
.sigure// (@Regenschauer490)
関西の大学院生
C++11歴8ヶ月の素人
const教穏健派
内容:
const の使い方を中心に、私が経験的に気づいたことを
コード例と一緒に説明していきます。
右下に「ここまでする必要があるのか?」を表すレベルを
私個人で恣意的につけてみました。
※ Lv 0 は C++を使う上で是非とも守るべきだと思うものです
2
3. const 概要
cv修飾子の一種
const 修飾子 : 変数の値を変更できないよう指示
volatile 修飾子 : 最適化せずメモリへアクセスするよう指示
変数に対する修飾とメンバ関数に対する修飾がある
const 修飾された変数の宣言時には初期化が必要
変数に対する修飾子の位置は 「const T」 「T const」
どちらでもOK
3
4. 定数
定数は, #define の代わりに constを使う
コンパイル時定数になるのは整数型だけ
その他は実行時定数となる
正直、定数は constexpr を使いましょう
4
const int N = 5; //コンパイル時定数
int ar1[N] = { 1, 2, 3, 4, 5 }; // Nは無くても可
array<int, N> ar2{ { 1, 2, 3, 4, 5 } }; // initializer-list
6. 関数の引数
T const& で引数をとる
引数元の内容が変更されないことを保証
Tがユーザ定義型(クラス)なら、値渡しより効率的
戻り値でローカル変数の参照(or ポインタ)を返すのはNG!
破棄されたローカル変数へアクセスしてしまう
moveで返そう
コンパイラが最適化してくれることもある(RVO/NRVO)
6
string AddGrass(string const& src){
auto tmp = src; // copy
tmp.append(“w”); //appendは非constな操作
return tmp; // move(tmp)
}
9. メンバ関数
constメンバ関数のオーバーロード
呼び出し元の変数(クラスオブジェクト)が・・・
const変数でない → 非constメンバ関数が呼び出される
const変数である → constメンバ関数が呼び出される
9
class ConstCheck {
public:
ConstCheck (){}
void Check (){ cout << "ないとき!" << endl; }
void Check () const{ cout << "あるとき!" << endl; }
};
ConstCheck ないとき;
ConstCheck const あるとき;
ないとき. Check(); //ないとき!
あるとき. Check(); //あるとき!
11. constの連鎖性11
struct Hoge2{
string str_;
Hoge2() = default;
};
void Foo(Hoge2 const& v){
v.str_.append(“”); // error :v.str _はconst変数
}
以上から分かるように、constは連鎖する(重要)
const 変数・メンバ関数からは、アクセスするメンバ変数は
const修飾され、非constメンバ関数は呼び出せない。
メンバ変数もconst修飾されていれば同様に制限がかかる。
オブジェクトの内側へどんどん伝搬していく
const_cast や mutable で部分的に破ることはできる
12. constの連鎖性(代入)12
int a = 0;
int const b = 0;
int& ra = a; // OK
int const& cra = a; // OK
int& rb = b; // error
int const& crb = b; // OK
int d = cra; // copy: T const& -> T
int const& e = []()->int{ return 1; }(); // T(pvalue) -> T const&
ポインタ・参照への代入時に、元の変数が・・・
const変数でない → ポインタ・参照のconst修飾は任意
const変数である → constポインタ・参照へのみ代入可
非ポインタ・参照への代入は コピーが行われる
別の変数が新たに作られるので、元の連鎖には関係しない
15. ポインタ
生ポインタと同じことがshared_ptrでもいえる
15
shared_ptr<T> const じゃ中身の値を変更できてしまう!
※私も以前にやらかしてしまいました (^^;)
typedef shared_ptr<string> StrPtr; とかしてるとやりがち
typedef shared_ptr<string const> C_StrPtr; も一緒に定義しよう
ptr自体がconstか
ptrが指す先を
const扱いするか
shared_ptr<T> const ptr ○ ☓
shared_ptr<T const> ptr ☓ ○
shared_ptr<T const> const ptr ○ ○
16. ポインタ
実際にコンパイルして確認
16
ConstCheck a , b;
ConstCheck * const p1 = &a; // p1自体がconst
ConstCheck const * p2 = &b; // p2が指す先をconst扱い
p1 = &b; // error
p1->Check (); //ないとき!
p2 = &a;
p2->Check (); //あるとき!
shared_ptr<ConstCheck> const s1(make_shared<ConstCheck>());
shared_ptr<ConstCheck const> s2(make_shared<ConstCheck>());
s1 = make_shared<ConstCheck>(); // error
s1->Check (); //ないとき!
s2 = make_shared<ConstCheck>();
s2->Check (); //あるとき!
17. ポインタ
※ ポインタ・参照先が絶対に不変だとは保証しない
指している先の変数自体は非constかもしれない
constなポインタ・参照を通しての変更が不可というだけ
17
int a = 0;
int const& cra = a;
a = 1;
cout << cra << endl; // 1
shared_ptr<int> s1(make_shared<int>(0));
shared_ptr<int const> s2(s1);
*s1 = 1;
cout << *s2 << endl; // 1
ポインタ(特にshared_ptr)を使う時は常に意識しておこう
18. ポインタ
const_cast によるcv修飾子の除去
constなポインタ・参照から、非constなポインタ・参照を得る
逆にconst を明示的に付与することもできる
よくある使用例
古いライブラリの関数へ引数を渡す時
constメンバ関数の実装を非constメンバ関数で流用する時
operator[]の実装を、operator[]constと統一したい時など
18
void Print(char* str){ printf("%s", str); }
string s = "example";
string const& crs = s;
string& rs = const_cast<string&>(crs);
rs = "change"; // OK
Print( const_cast<char*>(crs.c_str()) );
19. ポインタ
ただし、正しく使わないと未定義動作を引き起こす
const_cast したい constポインタ(参照)の指す先が・・・
const変数でない → cast後に指す先を変更してもOK
const変数である → cast後に指す先を変更すると爆死
そもそもconst_castを使う時点で設計ミス (とよく言われる)
※自分ではどうしようもない時以外、基本的に使わないでFA
19
int const b = 0; // 元の変数はconst
int const& crb = b;
int& rb = const_cast<int&>(crb);
rb = 1; // Undefined Behavior
21. ローカル変数
他にも同様に見ていきましょう
21
vector<Cookie> cookies{ Cookie::CHOCOLATE, Cookie::GOLDEN, Cookie::RED };
int sum = 0; // poor
for(auto e : cookies){
sum += static_cast<int>(e);
}
vector<Cookie> const cookies{ Cookie::CHOCOLATE, Cookie::GOLDEN, Cookie::RED };
auto const sum = boost::accumulate(cookies, 0,
[](int const sum, Cookie const ck){ return sum + static_cast<int>(ck); }
);
★const意識したらコードがきれいになりました
(23歳 男性 大学院生)
24. 戻り値
メンバ変数の参照を渡したい場合
参照や shared_ptr を返す戻り値は const修飾しておこう
shared_ptr 以外では、元のインスタンスの生存期間に注意
「メンバ変数を外部に晒すなんて・・・」 と一見思うかもしれ
ないが、const修飾しておけば外部からの変更はできない。
const_cast という があるので、絶対ではない
24
class CCMaker{
shared_ptr<ConstCheck> cc_;
public:
//外部からは Read Only な操作しかできない
shared_ptr<ConstCheck const> Get() const{ return cc_; }
};
不変性
コ ン ス ト
破 壊
ブレイカー