effective modern c++ 勉強会#3 item 15

59
Effective Modern C++ 勉強会#3 Item 15 刈谷 満(@kariya_mitsuru) 2015/3/25

Upload: mitsuru-kariya

Post on 23-Feb-2017

509 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: Effective Modern C++ 勉強会#3 Item 15

Effective Modern C++勉強会#3Item 15

刈谷 満(@kariya_mitsuru)

2015/3/25

Page 2: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

「市民、あなたは constexprですか」

「もちろんです、C++。constexprであることは市民の義務です」

© ボレロ村上

Page 3: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

constexpr -混乱しやすい新語オブザC++11

最高金賞受賞!!!

constに毛の生えたようなの、と思っていませんか?

オブジェクトに使えばだいたい合ってる。

単なる定数じゃなくてコンパイル時に分かる定数

Page 4: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

constexpr -混乱しやすい新語オブザC++11

最高金賞受賞!!!

constに毛の生えたようなの、と思っていませんか?

でも関数に使うと…

関数の結果が定数なわけでもましてやコンパイル時定数とも限らない。

Page 5: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

でもまずは、

constexprオブジェクトから。

Page 6: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

constexprオブジェクトとは

1. constオブジェクト

2. だけじゃない!コンパイル時に値が分かるオブジェクト!

ホントはコンパイル時じゃなくてリンク時でいい時もあるんだけど…

Page 7: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

constexprオブジェクトの特徴

• ROMに置ける。⇒特に組み込みシステムでは重要な機能

• "integral constant expression" が要求される箇所で使用することができる。⇒例えば、配列サイズ、整数テンプレート実引数(std::array の長さとか)、列挙子の値、アラインメント指定、などなど。

Page 8: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.int sz;

constexpr auto arraySize1 = sz;

std::array<int, sz> data1;

constexpr auto arraySize2 = 10;

std::array<int, arraySize2> data2;

エラー!sz の値は

コンパイル時には分からない

OK!10 はコンパイル時定数

OK!arraySize2 は constexpr

非 constexpr 変数

Page 9: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

一応注意しておくと、

単なる const じゃダメだよ。

Page 10: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

int sz;

const auto arraySize = sz;

std::array<int, arraySize> data;

非 constexpr 変数(前と一緒)

OK!arraySize は

sz の const コピー

エラー!arraySize の値は

コンパイル時には分からない

Page 11: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

簡単にいえば、constexpr オブジェクトは const だけど、

逆は必ずしも真では無い。

もしコンパイル時定数が欲しければ、const じゃなくて constexpr を使う事。

Page 12: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

簡単にいえば、constexpr オブジェクトは const だけど、

逆は必ずしも真では無い。

もしコンパイル時定数が欲しければ、const じゃなくて constexpr を使う事。

(余談)あれ?今まで const 変数で配列のサイズとかテンプレート引数とか

指定してきたような~?

Page 13: Effective Modern C++ 勉強会#3 Item 15

だけどOK!でも何で?だからOK!

Item 15: Use constexpr whenever possible.

const int arraySize = 10;

std::array<int, arraySize> data;

constexpr 変数じゃないconstexpr 変数じゃないけどコンパイル時定数!

Page 14: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

簡単にいえば、constexpr オブジェクトは const だけど、

逆は必ずしも真では無い。

もしコンパイル時定数が欲しければであることを保証したければ、const じゃなくて constexpr を使う事。

Page 15: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)ちなみに…

constexpr なオブジェクトは初期化無しでは宣言できません!

constexpr int var; エラー!初期化子がない!

Page 16: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)ちなみに…

constexpr な静的オブジェクトは普通の const なオブジェクトと一緒でデフォルト内部リンケージ!

だから、ヘッダで定義しておくと実体がいっぱいできたりする。

(事がある。ODR-used の関係で実体が出来ないことも多いけど…)

だからと言って、ヘッダファイルで extern 指定して外部リンケージにすると、リンク時にぶつかる…

Page 17: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

さてお待ちかね、constexpr な関数です。

constexpr 関数に、コンパイル時定数だけを与えると…

⇒コンパイル時定数!コンパイル時定数が必要な所で使える!

constexpr 関数に、コンパイル時定数以外も与えると…

⇒普通(コンパイル時定数ではない)コンパイル時定数が必要な所で使うとエラー!

Page 18: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

さてお待ちかね、constexpr な関数です。

constexpr 関数に、コンパイル時定数だけを与えると…

⇒コンパイル時定数!コンパイル時定数が必要な所で使える!

constexpr 関数に、コンパイル時定数以外も与えると…

⇒普通(コンパイル時定数ではない)コンパイル時定数が必要な所で使うとエラー!

(余談)…実はそうとも限らないんだけど…

Page 19: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)引数がコンパイル時定数でもダメな例

constexpr int div(int numer, int denom) {return numer / denom;

}

constexpr int i1 = div(1, 0);

constexpr int lshift(int num, int bit) {return num << bit;

}

constexpr int i2 = lshift(1, -1);

エラー!0 除算はUB!

エラー!マイナスシフトはUB!

Page 20: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

例えば…

様々な条件下での実験結果をstd::arrayに格納したい!

かつ

条件の種類はn種類あって、それぞれ3通りの状態がある!

std::pow(3, n) でおk?

Page 21: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

例えば…

様々な条件下での実験結果をstd::arrayに格納したい!

かつ

条件の種類はn種類あって、それぞれ3通りの状態がある!

std::pow(3, n) でおk?

Page 22: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

ダメな理由

その1:std::pow は浮動小数点型!

⇒std::size_t にキャストすればいんじゃね?

その2:std::pow は constexpr じゃない!

⇒つらぽよ…

Page 23: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

ダメな理由

その1:std::pow は浮動小数点型!

⇒std::size_t にキャストすればいんじゃね?

その2:std::pow は constexpr じゃない!

⇒つらぽよ…

じゃあsprout::pow 使おう!!!

じゃあsprout::pow 使おう!!!

作ろう!!!

Page 24: Effective Modern C++ 勉強会#3 Item 15

pow は constexpr 関数

Item 15: Use constexpr whenever possible.constexpr

int pow(int base, int exp) noexcept

{

}

こんなの作れば、std::array のサイズ指定も怖くない!

constexpr auto numConds = 5;

std::array<int, pow(3, numConds)> results;

pow は constexpr 関数ちなみに例外も吐かないよ

実装は後のお楽しみだ。クックックッ…

results の要素数は3 の numConds 乗!

Page 25: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.しかも、これなら実行時にもお使い頂けます!

auto base = readFromDB(“base”);

auto exp = readFromDB(“exponent”);

auto baseToExp = pow(base, exp);

値を実行時に取得すると…

pow 関数も実行時に呼び出し!

Page 26: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.しかも、これなら実行時にもお使い頂けます!

auto base = readFromDB(“base”);

auto exp = readFromDB(“exponent”);

auto baseToExp = pow(base, exp);

値を実行時に取得すると…

pow 関数も実行時に呼び出し!

TMPとは違うのだよ、

TMPとは

© ランバ・ラル

Page 27: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

で、どうやって書くの?

C++11 だと…

関数本体に書ける実行文は return 文 1 つだけ!

え~と…でも、まあ、

if 文使えなくても条件演算子 ?: があるし、

ループ書けなくても再帰があるし、

return 文 1 つだけでも関係ないよね!

Page 28: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

constexpr int pow(int base, int exp) noexcept

{

return (exp == 0 ? 1 : base * pow(base, exp - 1));

}

このくらいだったら、まぁ何とか読み書きできるけど、ちょっと複雑になったら辛いなぁ…

Page 29: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.ちなみに、C++14 なら…

constexpr int pow(int base, int exp) noexcept // C++14

{

auto result = 1;

for (int i = 0; i < exp; ++i)

result *= base;

return result;

}

見える、見えるぞ…

私にも、コードが見える!

Page 30: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

実はいろいろ制約あり

引数と戻り値の型は「リテラル型」じゃないとダメなんです。

リテラル型って何ぞ?

平たく言うと、コンパイル時定数になれる型のこと。

Page 31: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

リテラル型

C++11 では、組み込み型は void を除いて全部OK!

ユーザ定義型も一定の条件を満たせばOK!

だってコンストラクタやメンバ関数も constexpr になれるから!

Page 32: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

リテラル型

C++11 では、組み込み型は void を除いて全部OK!

ユーザ定義型も一定の条件を満たせばOK!

だってコンストラクタやメンバ関数も constexpr になれるから!

…どゆこと?

Page 33: Effective Modern C++ 勉強会#3 Item 15

class Point {public:

constexpr Point(double xVal = 0, double yVal = 0) noexcept: x(xVal), y(yVal) {}

constexpr double xValue() const noexcept { return x; }constexpr double yValue() const noexcept { return y; }

void setX(double newX) noexcept { x = newX; }void setY(double newY) noexcept { y = newY; }

private:double x, y;

};

Item 15: Use constexpr whenever possible.

Page 34: Effective Modern C++ 勉強会#3 Item 15

class Point {public:

constexpr Point(double xVal = 0, double yVal = 0) noexcept: x(xVal), y(yVal) {}

constexpr double xValue() const noexcept { return x; }constexpr double yValue() const noexcept { return y; }

void setX(double newX) noexcept { x = newX; }void setY(double newY) noexcept { y = newY; }

private:double x, y;

};

Item 15: Use constexpr whenever possible.

constexpr コンストラクタにコンパイル時定数を渡すと…

全てのメンバ変数がコンパイル時定数になる!

Page 35: Effective Modern C++ 勉強会#3 Item 15

class Point {public:

constexpr Point(double xVal = 0, double yVal = 0) noexcept: x(xVal), y(yVal) {}

constexpr double xValue() const noexcept { return x; }constexpr double yValue() const noexcept { return y; }

void setX(double newX) noexcept { x = newX; }void setY(double newY) noexcept { y = newY; }

private:double x, y;

};

Item 15: Use constexpr whenever possible.

constexpr コンストラクタにコンパイル時定数を渡すと…

全てのメンバ変数がコンパイル時定数になる!

オブジェクト自体もコンパイル時定数に

なる!

Page 36: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.constexpr Point p1(9.4, 27.7);

constexpr Point p2(28.8, 5.3);

constexpr

Point midpoint(const Point& p1, const Point& p2) noexcept

{

return { (p1.xValue() + p2.xValue()) / 2,

(p1.yValue() + p2.yValue()) / 2 };

}

constexpr auto mid = midpoint(p1, p2);constexpr オブジェクトを

constexpr 関数の結果で初期化!コンパイル時定数!!!

constexprメンバ関数を呼ぶ!

constexpr コンストラクタはコンパイル時に走る!コンパイル時定数!!!

Page 37: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.constexpr Point p1(9.4, 27.7);

constexpr Point p2(28.8, 5.3);

constexpr

Point midpoint(const Point& p1, const Point& p2) noexcept

{

return { (p1.xValue() + p2.xValue()) / 2,

(p1.yValue() + p2.yValue()) / 2 };

}

constexpr auto mid = midpoint(p1, p2);constexpr オブジェクトを

constexpr 関数の結果で初期化!コンパイル時定数!!!

constexprメンバ関数を呼ぶ!

constexpr コンストラクタはコンパイル時に走る!コンパイル時定数!!!

素晴らしい!

Page 38: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

今までコンパイル時 ------ 超えられない壁 ------ 実行時

今境界は曖昧に…

Page 39: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

今までコンパイル時 ------ 超えられない壁 ------ 実行時

今境界は曖昧に…

実行時処理をコンパイル時処理にマイグレーションすれば、

実行速度はより速くなる!!!

Page 40: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

今までコンパイル時 ------ 超えられない壁 ------ 実行時

今境界は曖昧に…

実行時処理をコンパイル時処理にマイグレーションすれば、

実行速度はより速くなる!!!でもコンパイル時間は長く…

Page 41: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

今までコンパイル時 ------ 超えられない壁 ------ 実行時

今境界は曖昧に…

実行時処理をコンパイル時処理にマイグレーションすれば、

実行速度はより速くなる!!!でもコンパイル時間は長く…

Page 42: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

今までコンパイル時 ------ 超えられない壁 ------ 実行時

今境界は曖昧に…

実行時処理をコンパイル時処理にマイグレーションすれば、

実行速度はより速くなる!!!でもコンパイル時間は長く…

Page 43: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)ちなみに…

constexpr コンストラクタの使い道はそれだけじゃない!

静的オブジェクトの初期化順の問題の一部を解決できる!(@cpp_akira さんありがとう!!!)

詳しくは Webで!http://cpprefjp.github.io/reference/mutex/mutex/op_constructor.html

(後でちょっとだけ出てくるよ!)

Page 44: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

ところで…

setX と setY は constexpr じゃないけど、

オブジェクトの値変更するんだから仕方ないよね…

Page 45: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

そんなメンバ関数も、constexpr になれる。

そう、

C++14 ならね。

Page 46: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.class Point {

public:

constexpr void setX(double newX) noexcept // C++14

{ x = newX; }

constexpr void setY(double newY) noexcept // C++14

{ y = newY; }

};

Page 47: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

C++11 で constexpr に出来なかった理由は…

その1:constexpr メンバ関数は暗黙で const 修飾!

だから、オブジェクトの状態は変更できない!

⇒C++14 からは暗黙の const 修飾は亡くなりました!

その2:戻り値の型 void はリテラル型じゃない!

⇒C++14では void もめでたくリテラル型になりました

Page 48: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)Caution!constexpr メンバ関数の暗黙 const 修飾は C++14 で亡くなるので、たとえ C++11 で書いていても constexpr メンバ関数には明示的にconst 修飾しておきましょう!!!

そうしないと、コンパイラが C++14 に移行した時に挙動が変わってしまいます!!!

(clang は 3.3 から警告が出ます)

Page 49: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

(余談)ちなみに…

constexpr な関数は暗黙で inline です!

(実際、極限のインライン化ですよね…)

だから、普通ヘッダで定義しておく。

(inline だから重複定義にならない)

あと、constexpr として使うには宣言だけじゃなく定義が必要!

Page 50: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

でも…

セッタ関数 constexpr で何が嬉しいの?

Page 51: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.こんな関数も書けるよ!

// 点 p の原点に対する鏡映を返す(C++14)

constexpr Point reflection(const Point& p) noexcept

{

Point result;

result.setX(-p.xValue());

result.setY(-p.yValue());

return result;

}

非 const の Point を作る

x と y を設定する

それのコピーを返す

Page 52: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.当然こんなこともできるよ!

constexpr Point p1(9.4, 27.7);

constexpr Point p2(28.8, 5.3);

constexpr auto mid = midpoint(p1, p2);

constexpr auto reflectedMid =

reflection(mid);

reflectedMid の値は(-19.1, -16.5) で、コンパイル時に分かる

このへんは前と一緒

Page 53: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

さて、本項目のアドバイスの理由は明らかになったでしょ?

1. constexpr オブジェクトと constexpr 関数は、非 constexpr

のものよりもより多くの文脈で使える。

2. constexpr を使う事で、あなたが作ったオブジェクトや関数の

使用可能なシチュエーションを最大化することができる。

Page 54: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

Caution!!!constexpr はオブジェクトや関数の

インタフェースの一部

だ!

constexpr、それは

"定数式が必要な箇所で使う事が出来る"

という宣言

Page 55: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

Caution!!!

constexpr なオブジェクトや関数を提供

クライアントコード内の定数式が必要な箇所で使われる

その後、constexpr を削除

クライアントコードのコンパイルエラーが大量発生!!!

Page 56: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

Caution!!!

デバッグやチューニングのために I/O を追加しよ

一般的に I/O は constexpr 関数内では許されていない

constexpr 取らなきゃ!!!

Page 57: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

Caution!!!

"whenever possible" は、

長期スパンで考えること!

Page 58: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

覚えておくこと

• constexpr オブジェクトは、const で、コンパイル中に分かる値で初期化される。

• constexpr 関数は、コンパイル時に分かる値を引数に呼び出すと、コンパイル時に結果を生成する。

• constexpr オブジェクトと関数は、非 constexpr オブジェクトと関数よりも、より幅広い文脈で使用することができる。

• constexpr はオブジェクトと関数のインタフェースの一部である。

Page 59: Effective Modern C++ 勉強会#3 Item 15

Item 15: Use constexpr whenever possible.

さあ、今日からあなたもconstexpr で

コンパイル時レイトレ!!!