📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
細 文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag… |
|||
| (同じ利用者による、間の7版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
C++には、<code>constexpr</code>という概念がある。<br> | C++には、<code>constexpr</code>という概念がある。<br> | ||
<br> | |||
今まで、const修飾してきたものには2種類ある。<br> | |||
* ROM化可能な値。 | |||
* 実行時にしか決まらないが、一度初期化した後は二度と変更されない値。 | |||
<br> | |||
C++ 11以降、前者はconstexprが受け持ち、後者はconstが受け持つことになった。<br> | |||
constexprは、constexprの制約を満たした変数の定義、関数と関数テンプレートの宣言、staticデータメンバの宣言に対して使用できる。<br> | |||
<br> | |||
つまり、constはRAMにしか配置できない変数に対して使う型修飾子となった。<br> | |||
constexprは型修飾子ではなく、型指定子である。<br> | |||
型を修飾するものでなく、ROM化できる、または、ROM化できる可能性がある、という意味を持つ指定子である。<br> | |||
<br> | <br> | ||
* constexprが使用できない、または、使用すべきではない場合 | * constexprが使用できない、または、使用すべきではない場合 | ||
| 18行目: | 29行目: | ||
<br><br> | <br><br> | ||
== | == constexpr変数 == | ||
constexpr変数は、<code>#define</code>等で作成するようなコンパイル時定数を作るためのキーワードである。<br> | |||
<br> | <br> | ||
constとは、「この変数は変更しないため、変更しようとする場合はコンパイルエラーにする」という合図である。<br> | |||
constexprとは、「この変数の値はコンパイル時に確定するため、確定しない場合はコンパイルエラーにする」という合図である。<br> | |||
<br> | <br> | ||
また、constexpr変数は、const変数としても扱われる。<br> | また、constexpr変数は、const変数としても扱われる。<br> | ||
<br> | <br> | ||
コンパイラは、constexpr変数の値をコンパイル時に計算しようとする。<br> | |||
もし、計算できなければ、コンパイルエラーを出力する。<br> | |||
<br> | <br> | ||
どの操作がコンパイル時に計算可能かは、関数にconstexprキーワードが付いているかどうかで判断される。(後述のセクションで記載する)<br> | どの操作がコンパイル時に計算可能かは、関数にconstexprキーワードが付いているかどうかで判断される。(後述のセクションで記載する)<br> | ||
< | <br> | ||
以下の例では、1行目でbufSizeを9に初期化している。<br> | |||
この演算はコンパイル時に行うため、constexpr変数を初期値9で宣言することにより、ROM上に配置される。<br> | |||
<syntaxhighlight lang="c++"> | |||
constexpr unsigned int bufSize = sizeof("abc def¥n"); | |||
static_assert(bufSize == 9, "bufSize is not 9"); | |||
char buf[bufSize] = {0}; | |||
fgets(buf, sizeof(buf), stdin); | |||
const int a = atoi(strtok(buf," ¥n")); | |||
const int b = atoi(strtok(nullptr," ¥n")); | |||
std::cout << (a + b) / 2 << std::endl; | |||
</syntaxhighlight> | |||
<br> | |||
以下の例では、列挙型Colorはint32_t型であり、3つの値をとる。<br> | |||
static宣言されたColor型の配列は、constexpr指定子が付加されて、ROM上に配置される。<br> | |||
<syntaxhighlight lang="c++"> | |||
typedef enum Color : int32_t | |||
{ | |||
Red = 0, | |||
Yellow, | |||
Blue | |||
} Color; | |||
constexpr static Color colors[] = | |||
{ | |||
Color::Red, | |||
Color::Yellow, | |||
Color::Blue | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
以下のサンプルコードでは、<u>標準入力から受け取る</u>操作がコンパイル時に行えないため、コンパイルエラーとなる。<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <iostream> | #include <iostream> | ||
| 59行目: | 105行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<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> | |||
下表に、ROM化できる条件を示す。なお、初期化する変数はconstexpr変数とする。<br> | |||
下表から分かることは以下の3つである。<br> | |||
* 渡す引数の指定子は、<code>const / constexpr</code>を指定する。 | |||
* 仮引数の指定子には、<code>指定子なし / const</code>を指定する。 | |||
* 関数の指定子は、必ず<code>constexpr</code>を指定する。<br> | |||
<center> | |||
{| class="wikitable" | |||
|- | |||
! 引数の指定子 !! 仮引数の指定子 !! 関数の指定子 !! コンパイルの可否 | |||
|- style="text-align:center;" | |||
| × || - || - || × | |||
|- style="text-align:center;" | |||
| - || - || × || × | |||
|- style="text-align:center;" | |||
| - || - || const || × | |||
|- style="text-align:center;" | |||
| - || constexpr || - || × | |||
|- style="text-align:center;" | |||
| const || × || constexpr || ○ | |||
|- style="text-align:center;" | |||
| const || const || constexpr || ○ | |||
|- style="text-align:center;" | |||
| constexpr || × || constexpr || ○ | |||
|- style="text-align:center;" | |||
| constexpr || const || constexpr || ○ | |||
|} | |||
</center> | |||
<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> | ||
| 101行目: | 215行目: | ||
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() | ||
| 124行目: | 238行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== constexprではない引数を与える ==== | |||
constexpr関数の結果は、常にコンパイル時に計算されるというわけではない。<br> | constexpr関数の結果は、常にコンパイル時に計算されるというわけではない。<br> | ||
<br> | <br> | ||
| 133行目: | 247行目: | ||
<br> | <br> | ||
constexprキーワードはあくまで「コンパイル時に値が確定できる」ことを伝えるだけで、「コンパイル時にしか値を計算しない」というわけではない。<br> | constexprキーワードはあくまで「コンパイル時に値が確定できる」ことを伝えるだけで、「コンパイル時にしか値を計算しない」というわけではない。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
| 164行目: | 278行目: | ||
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> | ||
| 213行目: | 327行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
==== 外部変数の参照 ==== | |||
constexpr関数で行なってはいけない操作は、主に引数以外のconstexpr以外の外部の変数を参照する操作である。<br> | constexpr関数で行なってはいけない操作は、主に引数以外のconstexpr以外の外部の変数を参照する操作である。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
| 268行目: | 382行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | |||
==== メンバ関数のconstexpr ==== | |||
メンバ関数にも<code>constexpr</code>を付けることができる。<br> | |||
< | <code>constexpr</code>を付ける基準は先ほどと同様、「コンパイル時に計算できるかどうか」である。<br> | ||
<syntaxhighlight lang="c++"> | |||
class CHoge | class CHoge | ||
{ | { | ||
| 280行目: | 395行目: | ||
auto f(int x, ...) { ... }; | auto f(int x, ...) { ... }; | ||
}; | }; | ||
</ | </syntaxhighlight> | ||
を、以下のように考えれば、constexprを付けるかどうかが判断できる。 | を、以下のように考えれば、constexprを付けるかどうかが判断できる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
class | class CHoge {...}; | ||
auto f(C& this, int x, ...) { ... }; | auto f(C& this, int x, ...) { ... }; | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
| 337行目: | 452行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | |||
==== 分割ファイルとconstexprとインライン ==== | |||
constexprが付いている関数は、コンパイル時に計算可能でなければならない。<br> | |||
コンパイル時というのはそれぞれの翻訳単位、すなわち、各ファイルをコンパイルしている場合でも関数が計算できなければならないということである。<br> | |||
<br> | |||
したがって、<code>constexpr</code>が付いている関数の宣言だけをして、別ファイルで実装を行うことはできない。<br> | |||
実装されているファイル以外をコンパイルしている時に、関数の内容が計算できないからである。<br> | |||
<br> | |||
つまり、<code>constexpr</code>が付いている関数は自動的にinline関数として扱われるということになる。<br> | |||
また、<code>constexpr</code>が付いている変数もinline変数として扱われる。<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cpp | |||
#include <iostream> | |||
#include "main.h" | |||
int main() | |||
{ | |||
// コンパイル可能 int CHoge1::f(int&)は通常のメンバ関数 | |||
auto c1 = CHoge1(41); | |||
auto x1 = 1; | |||
std::cout << c1.f(x1) << std::endl; | |||
// コンパイルエラー int CHoge2::f(int&)はconstexprであるが実装が無い | |||
// auto c2 = CHoge2(41); | |||
// auto x2 = 1; | |||
// std::cout << c2.f(x2) << std::endl; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.h | |||
class CHoge1 | |||
{ | |||
private: | |||
int a; | |||
public: | |||
constexpr CHoge1(int a) : a(a) {} | |||
int f(int& x); | |||
}; | |||
class CHoge2 | |||
{ | |||
private: | |||
int a; | |||
public: | |||
constexpr CHoge2(int a) : a(a) {} | |||
constexpr int f(int& x); | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// sub.cpp | |||
#include "main.h" | |||
int CHoge1::f(int& x) | |||
{ | |||
x += a; | |||
return x; | |||
} | |||
constexpr int CHoge2::f(int& x) | |||
{ | |||
x += a; | |||
return x; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== constexprテンプレート関数 ==== | |||
constexprが付いているテンプレート関数の場合、その関数を実体化した時にconstexpr関数として不適格な場合でもコンパイルエラーにはならず、非constexpr関数として扱われる。<br> | |||
以下のサンプルコードの場合、print_and_get関数はどのように実体化してもconstexpr関数にはならないが、コンパイルエラーにもならずに、constexprキーワードが無視される。<br> | |||
<br> | |||
つまり、テンプレート関数にconstexprキーワードが付いていても、設計者は「関数はconstexpr関数ではないかもしれない」と身構えなければならない。<br> | |||
<br> | |||
おそらく、「テンプレートのいくつかの場合はconstexpr、他の場合はconstexprではないような関数を作成したい」という需要があるからだと推測する。<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <iostream> | |||
// コンパイルエラー constexprではない関数 | |||
// constexpr auto print_and_get_int(int t) | |||
// { | |||
// std::cout << t << std::endl; | |||
// int v = 0; | |||
// std::cin >> v; | |||
// | |||
// return v; | |||
// } | |||
// コンパイル可能 constexprではないテンプレート関数 | |||
// constexprを付けているがコンパイルできる | |||
template<typename T> | |||
constexpr auto print_and_get(T t) | |||
{ | |||
std::cout << t << std::endl; | |||
int v = 0; | |||
std::cin >> v; | |||
return v; | |||
} | |||
int main() | |||
{ | |||
const auto a = print_and_get(42); // コンパイル可能 constexprキーワードは無視される | |||
// constexpr auto b = print_and_get(42); // コンパイルエラー constexprで受けるとコンパイルエラー | |||
std::cout << a << /* ", " << b << */ std::endl; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== constexprが付いているラムダ式 ==== | |||
ラムダ式(のoperator())も、関数と同様にconstexprを指定することができる。(指定しなくとも自動的にconstexprが付けてくれる)<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <iostream> | |||
// 標準入力からint型の値を受け取って返す(コンパイル時に値が定まらない)関数 | |||
auto get_value_from_stdin() | |||
{ | |||
int v; | |||
std::cin >> v; | |||
return v; | |||
} | |||
int main() | |||
{ | |||
// 常に42を返すラムダ式 | |||
auto f = []{ return 42; }; | |||
// 常に42を返すconstexprを明示したラムダ式 | |||
auto f_constexpr = []() constexpr { return 42; }; | |||
// constexprが付いている変数はキャプチャ可能 | |||
constexpr auto outer_value = 42; | |||
auto f_capture = [&outer_value]{ return outer_value; }; | |||
// constexprが付いている変数をキャプチャした場合は、constexpr関数にはならない | |||
const auto outer_value2 = get_value_from_stdin(); | |||
auto f_bad = [&outer_value2]{ return outer_value2; }; | |||
constexpr auto a = f(); // constexpr関数であることを明示しなくても使用可能 | |||
constexpr auto b = f_constexpr(); // constexpr関数であることを明示しても使用可能 | |||
constexpr auto c = f_capture(); // コンパイル可能 この関数はconstexpr関数 | |||
// constexpr auto d = f_bad(); // コンパイルエラー この関数はconstexpr関数ではない | |||
std::cout << a << ", " << b << ", " << c << /* ", " << d << */ std::endl; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
== レビュー == | |||
# constが付加された変数を見つけた場合、その変数はRAMにしか配置できないものか確認する。<br>もし、ROM化できる場合、constexpr指定子に変更できないか考えて指摘する。 | |||
# constexprテンプレート関数の内部に、ROM化できない文や関数呼び出しが入っていないか確認する。 | |||
# ESCRでは再帰は使用してはいけないので、constexpr関数内において、再帰を使用してはいけない。 | |||
# constexpr関数やconstexpr関数テンプレートを見つけた場合、その戻り値で初期化している変数とconstexpr型指定されているかどうか確認する。<br>そうでなければ、constexpr関数やconstexpr関数テンプレートの存在意義がない。 | |||
<br> | |||
以下の例は、constexprを付加しても意味の無い例である。<br> | |||
ただし、コンパイルエラーにはならない。<br> | |||
<syntaxhighlight lang="c++"> | |||
template <typename T> | |||
constexpr inline static T GetFromCin() | |||
{ | |||
T value; | |||
cin >> value; | |||
return value; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== まとめ == | |||
* constexprは、変数と関数で挙動が大きく異なるので、同一視しない。 | |||
* constexpr関数とconst修飾子にあまり関係がない点にも注意する。 | |||
* 複雑な関数の場合、「外部に影響を与えるか」「inlineにできるか」の2点を考えれば、その関数がconstexprが必要かどうか分かる。 | |||
* constexprを付けることができる関数の幅は広いので使用するべきである。<br>ただし、テンプレート関数をconstexprにするときは注意が必要である。 | |||
<br><br> | |||
{{#seo: | |||
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 | |||
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | |||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C++]] | [[カテゴリ:C++]] | ||