12,982
回編集
108行目: | 108行目: | ||
<br><br> | <br><br> | ||
== | == constexpr関数 == | ||
==== | ==== constexpr関数の基本 ==== | ||
もし、コンパイル時において、関数の引数にconstexpr変数を使用する場合、戻り値でconstexpr変数を初期化する文はコンパイルできる。<br> | |||
もし、コンパイル時において、関数の引数にconstexprでない変数を使用する場合、戻り値でconstexpr変数を初期化する文はコンパイルエラーになる。<br> | |||
<br> | <br> | ||
constexpr関数の引数にconstexpr変数を与えて、戻り値を使用してconstexpr変数を初期化する文はコンパイルできる。<br> | |||
それに対し、constexpr関数の引数に実行時に決まる変数を与えて、戻り値を使用してconstexpr変数を初期化するとコンパイルエラーになる。<br> | |||
これは、コンパイル時に値を決めることができないからである。<br> | |||
< | <syntaxhighlight lang="c++"> | ||
constexpr int twice(const int n) | |||
{ | |||
return n * 2; | |||
} | |||
constexpr int ten1 = 10; // コンパイル可能 | |||
constexpr int ten2 = twice(ten1); // コンパイル可能 | |||
constexpr int ten3 = twice(static_cast<int>(rand() % 10)); // コンパイルエラー | |||
</syntaxhighlight> | |||
<br> | |||
以下の例では、constexpr関数の引数にconstexprでない変数を与えて、その戻り値でconstexpr変数を初期化しているため、コンパイルエラーになる。<br> | |||
これは、演算結果がROM化できないからである。<br> | |||
それに対し、constexpr関数の引数にconstexprでない変数を与えて、その戻り値でconstexprでない変数を初期化する場合は、コンパイルエラーにならない。<br> | |||
<syntaxhighlight lang="c++"> | |||
constexpr int twice(const int n) | |||
{ | |||
return n * 2; | |||
} | |||
int ten = 10; | |||
constexpr int ten2 = twice(ten); // コンパイルエラー | |||
const int ten3 = twice(ten); // コンパイル可能 | |||
</syntaxhighlight> | |||
<br> | |||
上記の例のように、constexpr関数の引数に何を渡すか、constexpr関数の戻り値で何を初期化するかによって、<br> | |||
コンパイル可能またはコンパイルエラーになる。<br> | |||
<br> | |||
これは、設計者に対して、ROM化できるソースコードを記述する促すことになる。<br> | |||
<br> | |||
==== constexpr変数への戻り値の代入 ==== | |||
constexprは関数にも付加することができる。<br> | |||
関数に付加するconstexprキーワードは、この関数はコンパイル時に計算できることを表している。<br> | |||
<br> | |||
constexpr変数に代入できる値は、コンパイル時に計算できる値だけである。<br> | |||
そのため、constexprキーワードの無い関数の戻り値をconstexpr変数にする場合、<br> | |||
コンパイラはこの値はコンパイル時には計算できないと考えて、コンパイルエラーを出力する。<br> | |||
また、constexprキーワードがある関数の戻り値においても、関数の内容を解析して、<br> | |||
同様に、コンパイル時には計算できない式が1つでもあれば、コンパイルエラーを出力する。<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <iostream> | #include <iostream> | ||
147行目: | 186行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
また、コンパイル時に計算されているかどうか確認する場合は、<code>static_assert</code>を使用する方法もある。<br> | また、コンパイル時に計算されているかどうか確認する場合は、<code>static_assert</code>を使用する方法もある。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
// 常に42を返す関数 | // 常に42を返す関数 | ||
auto answer() | auto answer() | ||
170行目: | 209行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== constexprではない引数を与える ==== | |||
constexpr関数の結果は、常にコンパイル時に計算されるというわけではない。<br> | constexpr関数の結果は、常にコンパイル時に計算されるというわけではない。<br> | ||
<br> | <br> | ||
179行目: | 218行目: | ||
<br> | <br> | ||
constexprキーワードはあくまで「コンパイル時に値が確定できる」ことを伝えるだけで、「コンパイル時にしか値を計算しない」というわけではない。<br> | constexprキーワードはあくまで「コンパイル時に値が確定できる」ことを伝えるだけで、「コンパイル時にしか値を計算しない」というわけではない。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
210行目: | 249行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== 引数や戻り値のconstとconstexpr関数 ==== | |||
変数のconstexprがconstを兼ねているので見づらいが、関数のconstexprは引数や返り値のconstとは一切関係が無い。<br> | 変数のconstexprがconstを兼ねているので見づらいが、関数のconstexprは引数や返り値のconstとは一切関係が無い。<br> | ||
<br> | <br> | ||
以下のサンプルコードでは、answer_constexpr3関数は「変数の参照を受け取り、破壊的変更を加えて、その参照をconstも付けずに返す」という操作を行っているが、<br> | 以下のサンプルコードでは、answer_constexpr3関数は「変数の参照を受け取り、破壊的変更を加えて、その参照をconstも付けずに返す」という操作を行っているが、<br> | ||
この操作は全て(少なくともC++14以降では)constexprで行ってよい操作なので、この関数は問題なくconstexpr関数として作ることができる。<br> | この操作は全て(少なくともC++14以降では)constexprで行ってよい操作なので、この関数は問題なくconstexpr関数として作ることができる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
259行目: | 298行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== 外部変数の参照 ==== | |||
constexpr関数で行なってはいけない操作は、主に引数以外のconstexpr以外の外部の変数を参照する操作である。<br> | constexpr関数で行なってはいけない操作は、主に引数以外のconstexpr以外の外部の変数を参照する操作である。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
314行目: | 353行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | |||
==== メンバ関数のconstexpr ==== | |||
メンバ関数にも<code>constexpr</code>を付けることができる。<br> | メンバ関数にも<code>constexpr</code>を付けることができる。<br> | ||
<code>constexpr</code>を付ける基準は先ほどと同様、「コンパイル時に計算できるかどうか」である。<br> | <code>constexpr</code>を付ける基準は先ほどと同様、「コンパイル時に計算できるかどうか」である。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
class CHoge | class CHoge | ||
{ | { | ||
326行目: | 366行目: | ||
auto f(int x, ...) { ... }; | auto f(int x, ...) { ... }; | ||
}; | }; | ||
</ | </syntaxhighlight> | ||
を、以下のように考えれば、constexprを付けるかどうかが判断できる。<br> | を、以下のように考えれば、constexprを付けるかどうかが判断できる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
class CHoge {...}; | class CHoge {...}; | ||
auto f(C& this, int x, ...) { ... }; | auto f(C& this, int x, ...) { ... }; | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
383行目: | 423行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== 分割ファイルとconstexprとインライン ==== | |||
constexprが付いている関数は、コンパイル時に計算可能でなければならない。<br> | constexprが付いている関数は、コンパイル時に計算可能でなければならない。<br> | ||
コンパイル時というのはそれぞれの翻訳単位、すなわち、各ファイルをコンパイルしている場合でも関数が計算できなければならないということである。<br> | コンパイル時というのはそれぞれの翻訳単位、すなわち、各ファイルをコンパイルしている場合でも関数が計算できなければならないということである。<br> | ||
394行目: | 434行目: | ||
つまり、<code>constexpr</code>が付いている関数は自動的にinline関数として扱われるということになる。<br> | つまり、<code>constexpr</code>が付いている関数は自動的にinline関数として扱われるということになる。<br> | ||
また、<code>constexpr</code>が付いている変数もinline変数として扱われる。<br> | また、<code>constexpr</code>が付いている変数もinline変数として扱われる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
// main.cpp | // main.cpp | ||
#include <iostream> | #include <iostream> | ||
413行目: | 453行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
< | <syntaxhighlight lang="c++"> | ||
// main.h | // main.h | ||
class CHoge1 | class CHoge1 | ||
436行目: | 476行目: | ||
constexpr int f(int& x); | constexpr int f(int& x); | ||
}; | }; | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
< | <syntaxhighlight lang="c++"> | ||
// sub.cpp | // sub.cpp | ||
#include "main.h" | #include "main.h" | ||
455行目: | 495行目: | ||
return x; | return x; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== constexprテンプレート関数 ==== | |||
constexprが付いているテンプレート関数の場合、その関数を実体化した時にconstexpr関数として不適格な場合でもコンパイルエラーにはならず、非constexpr関数として扱われる。<br> | constexprが付いているテンプレート関数の場合、その関数を実体化した時にconstexpr関数として不適格な場合でもコンパイルエラーにはならず、非constexpr関数として扱われる。<br> | ||
以下のサンプルコードの場合、print_and_get関数はどのように実体化してもconstexpr関数にはならないが、コンパイルエラーにもならずに、constexprキーワードが無視される。<br> | 以下のサンプルコードの場合、print_and_get関数はどのように実体化してもconstexpr関数にはならないが、コンパイルエラーにもならずに、constexprキーワードが無視される。<br> | ||
465行目: | 504行目: | ||
<br> | <br> | ||
おそらく、「テンプレートのいくつかの場合はconstexpr、他の場合はconstexprではないような関数を作成したい」という需要があるからだと推測する。<br> | おそらく、「テンプレートのいくつかの場合はconstexpr、他の場合はconstexprではないような関数を作成したい」という需要があるからだと推測する。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
499行目: | 538行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== constexprが付いているラムダ式 ==== | |||
ラムダ式(のoperator())も、関数と同様にconstexprを指定することができる。(指定しなくとも自動的にconstexprが付けてくれる)<br> | ラムダ式(のoperator())も、関数と同様にconstexprを指定することができる。(指定しなくとも自動的にconstexprが付けてくれる)<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
540行目: | 579行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||