Arantium Maestum

プログラミング、囲碁、読書の話題

Effective C++勉強メモ: Item 20 Pass-by-Const-Reference

Pass-by-valueとpass-by-const-referenceのどちらか迷った時は大抵pass-by-const-referenceを選ぼう、という項目。

void printName(MyObj o)
{
  std::cout << o.name();
}

void printName(const MyObj& o)
{
  std::cout << o.name();
}

前者がpass-by-value、後者がpass-by-const-reference。

pass-by-valueの場合、引数となるオブジェクト自身のメンバデータに加え、メンバデータがポインタだった場合などは大抵そのポインタ先のデータもすべてコピーされ、そして関数から出る時に破棄される。その代わり関数の中でconstではないメンバ関数や処理を使っても引数の元のオブジェクトは変わらない。

しかし、もし関数の中の処理が引数のオブジェクトに対してはconstなものしか使わない場合、pass-by-const-referenceすることでいちいちコピー、破棄するコストを避けることができる。(個人的にはうっかりconstじゃない処理を書いてしまった場合はcompiler errorなのも嬉しい)

さらに、C++がpass-by-base-class-valueして作成されるコピーには、継承先のクラスのデータが欠如してしまうという特徴がある:

class A
{
  public:
    int a, b, c;
    virtual const std::string& name();
}

class X : public A
{
  public:
    std::string m_name;
    const std::string& name() { return m_name; }
}

void f(A o)
{
  std::cout << o.name();
}

X x();
f(x);

このコードはエラー。何故ならf関数の中でオブジェクトoはAのメンバデータa、b、cは持つがXのデータであるm_nameはコピーされず、name関数が存在しないm_nameを返そうとするから。ポインタなら問題ないが、この場合はnameがconst std::string&を返すのでfをpass-by-constにしても問題なくnameが使える。

Pass-by-const-reference非推奨なのはコピーが絶対に安いデータ。built-in型とstlイテレータ、関数オブジェクトがそれだ。これらはpass-by-const-referenceよりもpass-by-valueの方が安い(const-referenceを作成するよりデータを素直にコピーしてしまう方がコストが低くて済む)。

それ以外は全部なるべくpass-by-const-reference。