(2018-04-22 追記)
はじめに
これは、私があるプログラムを作るために少し実装をして、うまく実装できているか試しにテストコードをコンパイルしてみた時に嵌ってしまった状況を書き記したものである。
間違っているところとかあったら指摘してください、気づいたら直すように努力します
あと初心者なのでいろいろガバガバな事やってるでしょうがご了承ください。
1 右往左往
最初はVS2017のバージョン不足、次はコンパイルオプションの設定忘れや設定方法の間違いでうまくいくわけもない状態だった。このときは、まだそれらをなおせばうまくいくと思っていたのだ。それが間違いとも知らずに…
2 理解できなかったエラー
VSのバージョンを上げ、オプションも設定できた。これで行ける!と思い、以下のコード(再現なので実際と違う可能性あり)をコンパイルした。
# include <array>
# include <iostream>
static constexpr int ENEMY_AIRCRAFT_MAX_PER_SLOT = 256;
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void);
class calc_AW {
private:
public:
static constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> table_num_remain_enemy = calc_enemy_remain_table();
};
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void) {
std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> temp = {{{}}};
int i=0,j=0,k=0;
for (i = 0; i < 11; i++) {
for (j = 0; j < 11; j++) {
for (k = 0; k < ENEMY_AIRCRAFT_MAX_PER_SLOT; k++) {
temp[k][j][i] = k - (int)(((0.65 * j) + (0.35 * i)) * k);
}
}
}
return temp;
}
int main(void) {
std::cout << calc_AW::table_num_remain_enemy[100][0][0] << std::endl;
return 0;
}
そして出てきたのは…
エラー C2131 式は定数に評価されませんでした AWCCC (フォルダ名)\awccc\awccc.cpp 13
であった。
そして、なぜそうなるのか理解できなかった。コードには宣言がなされていたのに…
考えて、std::arrayのネストに対してoprrator[]を復数つかていたのが原因でないかと思いついて、コードを以下のように書き換えた。
# include <array>
# include <iostream>
static constexpr int ENEMY_AIRCRAFT_MAX_PER_SLOT = 256;
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void);
class calc_AW {
private:
public:
static constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> table_num_remain_enemy = calc_enemy_remain_table();
};
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void) {
std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> temp = {{{}}};
std::array<std::array<int, 11>, 11> tp1 = { {} };
std::array<int, 11> tp2 = {};
int i=0,j=0,k=0;
for (i = 0; i < ENEMY_AIRCRAFT_MAX_PER_SLOT; i++) {
for (j = 0; j < 11; j++) {
for (k = 0; k < 11; k++) {
tp2[k] = i - (int)(((0.65 * j) + (0.35 * k)) * i);
}
tp1[j] = tp2;
}
temp[i] = tp1;
}
return temp;
}
int main(void) {
std::cout << calc_AW::table_num_remain_enemy[100][0][0] << std::endl;
return 0;
}
しかし、無情にも
エラー (アクティブ) E0028 式には定数値が必要です AWCCC (フォルダ名)\AWCCC\AWCCC.cpp 13
エラー C2131 式は定数に評価されませんでした AWCCC (フォルダ名)\awccc\awccc.cpp 13
とエラーが出てしまった、。
ここでようやくビルドログを見たところ、
1>------ ビルド開始: プロジェクト: AWCCC, 構成: Debug Win32 ------
1>AWCCC.cpp
1>(フォルダ名)\awccc\awccc.cpp(13): error C2131: 式は定数に評価されませんでした
1>(フォルダ名)\awccc\awccc.cpp(13): note: エラーの原因は未定義関数の呼び出しまたは 'constexpr' が宣言されていない呼び出しです
1>(フォルダ名)\awccc\awccc.cpp(13): >note: 'calc_enemy_remain_table' の使用量を参照してください
1>プロジェクト "AWCCC.vcxproj" のビルドが終了しました -- 失敗。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
となっていた。
前方宣言も実態も作っていたのになぜ?という思いだった。
3 コンパイラを変えて
理解ができなかったので、Wandboxでgccを使って試してみることにした。すると、
Start
prog.cc:13:144: error: 'constexpr std::array< std::array< std::array< int, 11>, 11>, 256> calc_enemy_remain_table()' used before its definition
static constexpr std::array< std::array< std::array< int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> table_num_remain_enemy = calc_enemy_remain_table();
> ^
1
Finish
と表示されていた。
そして、定義の前に使われているというエラーが出ていることに気づく。
そこで、以下のように修正してみたところ、エラーは出なくなった。
# include <array>
# include <iostream>
static constexpr int ENEMY_AIRCRAFT_MAX_PER_SLOT = 256;
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void);
constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> calc_enemy_remain_table(void) {
std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> temp = {{{}}};
std::array<std::array<int, 11>, 11> tp1 = { {} };
std::array<int, 11> tp2 = {};
int i=0,j=0,k=0;
for (i = 0; i < ENEMY_AIRCRAFT_MAX_PER_SLOT; i++) {
for (j = 0; j < 11; j++) {
for (k = 0; k < 11; k++) {
tp2[k] = i - (int)(((0.65 * j) + (0.35 * k)) * i);
}
tp1[j] = tp2;
}
temp[i] = tp1;
}
return temp;
}
class calc_AW {
private:
public:
static constexpr std::array<std::array<std::array<int,11>, 11>, ENEMY_AIRCRAFT_MAX_PER_SLOT> table_num_remain_enemy = calc_enemy_remain_table();
};
int main(void) {
std::cout << calc_AW::table_num_remain_enemy[100][0][0] << std::endl;
return 0;
}
そして、VS2017でも同じように正常にビルドされた。
おわりに
constexpr関数の位置によってコンパイルエラーが発生していたことは分かった。だが、constexprで相互再帰ができるらしいことを知っている。前方宣言があるにもかかわらず、このコンパイルエラーはなぜ起きたのだろうか。私には、わからない…
追記
shortheronさんが、
constexpr変数の初期化子はconstant expressionである必要があるが、
未定義のconstexpr関数の呼び出しはconstant expressionでない
ということを教えてくれました。
constexpr変数の初期化にconstexprを使う際は定義が前にないといけないことを教えていただき、ありがとうございました!
補足
エラーメッセージの表示がおかしくなっていたため一部加工があります。
ご了承ください。