C++11の機能 (constexpr, 右辺値参照・ムーブセマンティクス)

C++11の機能を紹介するシリーズ第三弾です。
今回は、constexprと右辺値参照・ムーブセマンティクスを紹介します。

constexpr

constexprはコンパイル時定数を表現するための機能です。
コンパイル時定数はコンパイル時には値が確定している値のことで、コンパイル時定数になっていると展開されたり先に計算が行われるなどがされ実行時の効率が向上する可能性があります。

constexprは変数、関数、コンストラクタにつけることができます。

constexpr変数

変数につける場合はconstの代わりに記述することができます。

constexpr int ZERO = 0;

このように記述するとZEROがコンパイル時定数になります。
コンパイル時定数はテンプレート引数として渡せます。

template<int N> void func() { ... }

// 使う
constexpr int ZERO = 0;

func<ZERO>();

constexpr関数

関数にconstexprをつけると定数式が必要な場合にコンパイル時に計算されます。
逆に言うと必要のない場合や引数がコンパイル時定数でない場合は実行時に普通の関数として使われます。
C++11のconstexpr関数はreturnはひとつだけifやwhileなどの制御分が使えないなど、かなり制限がきついので、C++11のレベルで使うのは余りおすすめできません。C++14以降になるとこの制限がだいぶ緩くなるのでかなり使いやすくなりました。

constexpr int square(int n) {
    return n * n;
}

右辺値参照とムーブセマンティクス

右辺値参照

右辺値参照はその名の通り右辺値を参照する参照です。
右辺値は簡単に言うと名前のないオブジェクトや破棄しても問題ないオブジェクトのことです。
この機能が登場するまでは右辺値も左辺値も同じ参照を使って参照していました。ですが、破棄しても問題ないオブジェクトと、破棄していけないオブジェクトが区別できないと、無駄な処理をしてしまうなどの問題があります。
これを区別するために登場した機能が右辺値参照です。
右辺値参照は&ではなく&&を付けます。

int&& x = 1;

ムーブセマンティクス

ムーブセマンティクスは破棄しても良いオブジェクトから別のオブジェクトに値を移すときにコピー以下のコストで値を移すことができるようにするための意味論です。

例として、vectorのようなクラスVecについてを考えます。
Vecは連続したメモリ空間の先頭へのポインタを持つものとします。
Vecのコピーを考えてみると、元のVecと同じサイズのメモリ領域を確保し、全ての要素のデータをコピーしてくることになります。そうなると1000要素あった場合は、1000要素分の領域確保と1000回のコピーを行うことになります。もしも、元のVecがそれ以後不要な値だった場合はこれらの操作は無駄になってしまいます。なぜなら、元のVecのポインタを自分のポインタと入れ替えてしまえば定数時間で処理が終了してしまうからです。
ここで、先程出てきた右辺値参照を使うと右辺値参照で参照されているということは破棄しても大丈夫と意思表示したものと考えることができます。破棄しても良い値ですから壊してもこれ以後使えなくなっても誰も文句は言いません。なので、右辺値参照を渡してきた場合にポインタの入れ替えにることで処理のムダを省くことができます。
この例を表したクラスを次に示します。(メモリの解放処理などは省いています)

template<class T> class Vec {
private:
    T *_ptr;
    std::size_t _size;

public:
    Vec(const Vec& v) : _ptr(new T[v._size]), _size(v._size) { // 要素分の領域確保
        std::copy(v._ptr, v._ptr + v._size, _ptr); // 要素分のコピー
    }
    Vec(Vec&& v) : _ptr(v._ptr), _size(v._size) { // vの領域をもらってくる
        v._ptr = nullptr; // vの領域を使えなくする
    }
};

値の移動をすることをムーブといいます。ムーブのコストはコピーのコスト以下になります。
ムーブセマンティクスはあくまで概念なので実際にムーブの処理を行わなければ効率的に動かすことはできません。
ムーブを実現するためにコンストラクタと代入演算子には追加でムーブをするためのオーバーロードが追加されています。それぞれ、ムーブコンストラクタとムーブ代入演算子と呼びます。
上記の例で出てきた自身と同一の型の右辺値参照を受けるコンストラクタをムーブコンストラクタと呼びます。同じく自身と同一の型の右辺値参照を受ける代入演算子をムーブ代入演算子と呼びます。
上記の例のようにムーブ処理をそれぞれの関数内で行うことでムーブを実現することができます。

コメント

  1. Slots by Pragmatic Play - AprCasino
    Pragmatic Play. Pragmatic Play. Pragmatic Play. Slot Machine. The Dog worrione.com House. Slots. febcasino Wild apr casino West Gold. Pragmatic Play. kadangpintar Jackpot Party. https://jancasino.com/review/merit-casino/

    返信削除

コメントを投稿

このブログの人気の投稿

初めの挨拶

C++11の機能 (型推論, 範囲for)

C++11の機能 (関数のdefault・delete宣言, overrideとfinal指定子, 移譲コンストラクタ, 継承コンストラクタ)