このページはC++11に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
メンバ関数のCV
修飾は、*this
がconst
/volatile
である場合とそうでない場合でメンバ関数をオーバーロードできる。
同じように、メンバ関数に対して&
もしくは&&
の参照修飾子を付加することで、*this
が左辺値である場合に呼び出されるメンバ関数のオーバーロード、*this
が右辺値である場合に呼び出されるメンバ関数のオーバーロードを定義できる。
これを使用することで、「一時オブジェクトな*this
に対して特定のメンバ関数を呼び出せてはならない制約」、「*this
が左辺値もしくは右辺値である場合の効率的な実装を使い分ける」といった設計ができるようになる。
struct X {
// 代入演算子は、*thisが左辺値である場合のみ呼び出せるようにする
X& operator=(const X&) &
{
return *this;
}
};
int main()
{
X lvalue;
lvalue = X(); // OK : lvalueオブジェクトは左辺値
//X() = lvalue; // コンパイルエラー!右辺値のオブジェクトに対して代入演算子は呼び出せない
}
参照修飾子は、CV修飾子と組み合わせて使用できる:
#include <iostream>
struct X {
int f() & // *thisが非constな左辺値である場合に呼び出される
{ return 1; }
int f() const & // *thisがconstな左辺値である場合に呼び出される
{ return 2; }
int f() && // *thisが右辺値である場合に呼び出される
{ return 3; }
};
int main()
{
X x;
const X cx;
std::cout << x.f() << std::endl; // 1
std::cout << cx.f() << std::endl; // 2
std::cout << X().f() << std::endl; // 3
}
仕様
- コンストラクタとデストラクタに対しては、参照修飾子を付加できない
-
CV修飾のみされたメンバ関数と、参照修飾のみされたメンバ関数ではオーバーロードできない
struct X { int f() const; int f() &; // コンパイルエラー! };
-
仮想関数は、基底クラスと派生クラスで、同じ参照修飾子を持たなくてはならない
例
#include <vector>
#include <utility>
class X {
std::vector<int> data_;
public:
X() : data_(100) {}
// *thisが左辺値の場合は、保持しているvectorオブジェクトへの参照を返す
const std::vector<int>& data() const&
{ return data_; }
// *thisが右辺値の場合は参照を返すと一時オブジェクトの寿命が尽きてしまうため、
// ムーブで返す
std::vector<int> data() &&
{ return std::move(data_); }
};
int main()
{
X x;
const std::vector<int>& v1 = x.data();
std::vector<int> v2 = X().data();
}
出力