Effective Modern C++
Item 12
Effective Modern C++ 勉強会#3
2015/03/25
福田圭祐 @keisukefukuda
Item 12:
Declare overriding functions
override
概要
基底クラスの関数をoverrideしたいときは、新たに追加
されたoverrideキーワードを使おう
(おまけ)
メンバ関数のreference qualifierについて
reference qualifierについて
先に説明
メンバ関数宣言にreference qualifierがついていると、
*thisが右辺値か左辺値かによって呼び分けられる関数を
作ることができる
class Widget {
public:
void f() &;
void f() &&;
};
Widget().f(); // f()&&
Widget w;
w.f(); // f() &
reference qualifierの例
class Widget {
public:
using DataType = std::vector<double>;
DataType &data() { return values; }
private:
DataType values;
};
...
// Widgetを作る関数
Widget makeWidget();
// ふつう
Widget w;
auto vals1 = w.data();
// Widgetオブジェクトは右辺値なので
// vectorのコピーが無駄
auto vals2 = makeWidget().data();
reference qualifierの例
class Widget {
public:
using DataType = std::vector<double>;
DataType &data() & { return values; }
DataType data() && {
return std::move(values);
}
private:
DataType values;
};
// lvalue overloadが呼ばれる
Widget w;
auto vals1 = w.data();
// rvalue overloadが呼ばれる
auto vals2 = makeWidget().data();
virtual関数のoverride
virtual関数のoverrideが起こる条件
(C++98 & C++11共通)
• 基底クラスの関数がvirtual
• 基底クラスと派生クラスで関数名が同一
• 仮引数の型も同一
• const qualifierも同一
• 戻り値の型と例外指定がcompatible
戻り値の型がcompatibleとは
基底クラスと派生クラスの関数
TB B::f()
TD D::f()
があるとき、以下の条件が満たされれば、両者の戻り値の
型はcovariantである
• TB,TDが両方ともポインタor参照型である
• TBが、TDの直接or間接の基底クラスである
• cv-qualifierが同一 or TBの方が厳しい
例外指定がcompatibleとは
基底クラスと派生クラスの関数
TB B::f() throws(…)
TD D::f() throws(…)
があるとき、
{ B::f()が投げる例外 } ⊃ {D::f()が投げる例外 }
なら、例外指定はcompatible
※ただし例外指定はオワコン
virtual関数のoverride
virtual関数のoverrideが起こる条件
(C++98 & C++11共通)
• 基底クラスの関数がvirtual
• 基底クラスと派生クラスで関数名が同一
• 仮引数の型も同一
• const qualifierも同一
• 戻り値の型と例外指定がcompatible
(C++11から)
• reference qualifierが同一
間違いの例
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
間違いの例
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
const qualifierが違う
間違いの例
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
const qualifierが違う
仮引数の型が違う
間違いの例
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
const qualifierが違う
仮引数の型が違う
reference qualifierが違う
間違いの例
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
const qualifierが違う
仮引数の型が違う
reference qualifierが違う
基底クラスでvirtual宣言を忘れている
virtual関数のoverrideは間違いやすい
でもコンパイラが警告出すでしょ?
→ 出さないコンパイラもある
mf1 mf2 mf3 mf4
g++ 4.8.2 warning※ warning※ warning※
g++ 4.9.2 warning※ warning※ warning※
clang++ 3.5 warning warning warning
※-Wall, -Wextra, -Weffc++(4.9.2)ではダメで、陽に-Woverloaded-virtualを指定
やってみた
※g++は、-Wsuggest-override というのも有るらしいが、4.9.2では認識されなかった
virtual関数のoverrideは間違いやすい
重要だが間違いやすいので、C++11ではoverrideキーワー
ドが追加された
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x) override;
virtual void mf3() && override;
void mf4() const override;
};
error: 'virtual void Drived::mf1()' marked override, but
does not override
virtual void mf1() override;
^
ちゃんとエラーになる
virtual関数のoverrideは間違いやすい
重要だが間違いやすいので、C++11ではoverrideキーワー
ドが追加された
// overrideを使った正しいコード
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
virtual void mf4() const;
};
class Drived : public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x) override;
virtual void mf3() && override;
void mf4() const override;
};
overrideを使うメリット
overrideキーワードを使うことのメリット:
• 間違いが減る
• 基底クラスのvirtual関数のシグネチャを変更する際に、影響範囲
の見積もりが楽
override, final は contextual keyword
=関数シグネチャの末尾以外では予約語ではない
class Widget {
public:
void override(); // →OK
};
Things to remember
• overrideキーワードを使おう
• reference qualifiersを使うと、*thisが左辺値と右
辺値の時の挙動を変えることができる

Effective modern C++ 勉強会 #3 Item 12

Editor's Notes

  • #7  ----- 会議メモ (3/25/15 19:24) ----- 来てクラスでの&の有無