関数の外側の「神秘的な」定数を削除した後の例について説明します。
- OS X 10.10とgcc 4.9.2およびclang 3.5 / 3.6
- clang 3.5を使用するUbuntu 14.10
- MinGW-w64-32およびgcc 4.9.2を搭載したWindows
私は他のシステムの下でさらに特別な何かを除外しません。 誰かが彼らについて教えてくれたら嬉しいです。
constexprは本当にすべてに対応していますか?
インスティテュートを卒業し、C ++開発者(たとえば、メインデスクトップシステム、Linix、OS X、Windows)として働くようになった学生は、会社の全員がすでにC ++ 11互換コンパイラに切り替えたことを知ります。 より簡単に、そしてより短く書く能力に満足して、ヒーローは次のようなヘッダーファイルの1つに書き込みます:
constexpr char sin_tables[4096] { /* , */};
OS Xでビルドをテストしている隣接部門からのコミットの約1時間後、バイナリが不良であるという戸惑う叫び声が聞こえます。 Visual C ++(特定の新しいバージョン)とは異なり、clang(3.5、3.6)もgcc(4.8、4.9)もこのような未定義の動作に依存していません(何らかの理由で、Microsoftコンパイラーはconstexprの内部リンクを含む通常の変数の表示を停止しました)そして、しかし、善行を行いました)、私たちは私たちの配列の複製を得ました。
例 :
非表示のテキスト
膝に書かれた例:
a.cpp
b.cpp
えー
a.cpp
#include <cstdio> #include "hh" void printSin1() { for (auto &e : sin_tables) printf("%f\n", e); } int main() { printSin1(); printSin2(); }
b.cpp
#include <cstdio> #include "hh" void printSin2() { for (auto &e : sin_tables) printf("%f\n", e); }
えー
constexpr float sin_tables[4096] { /* ... */}; void printSin1(); void printSin2();
収集するのが面倒な人のために :
非表示のテキスト
OS Xでのコンパイラの特定の作業が非難であると思われる場合は、お急ぎください。UbuntuおよびMinGWの環境にもあります。 これは、clangおよびgccコンパイラのグローバル機能です...
そして、あなたが古い「証明された」方法を試してみると?
ここに不幸があります、私たちの若いプログラマは考えました。 たぶん、これらのペンギンポピーのハックを書いてください。 すぐに言ってやった。
#if !defined(CONST_UNIQUE) #if defined(MSVC) #define CONST_UNIQUE constexpr #else #define CONST_UNIQUE extern __attribute__((weak)) constexpr #endif #endif
すべてが機能し、すべてが進んでいます。 しかし、数分後、同じ部屋から新しい叫び声が出てきました。コミットが機能していない、ビルドボットがエラーを投げました、何をしているのですか?! ここでも、問題は珍しいことではありません。 Clangは、gccですべての非標準を実装しようと試みていますが、完全に互換性があることはなく、そうなる可能性は低いです。 したがって、この属性を使用する特殊なケースでは、驚きが待っています(これがないとどうなるでしょう)。
CONST_UNIQUE int A = 137; // gcc OK, clang OK CONST_UNIQUE int B = A+1; // gcc OK, clang OK #include <array> std::array<int, A> loveClang; // gcc OK, clang FAIL
./file.cpp:21:17: error: non-type template argument is not a constant expression std::array<int, A> loveClang; ^
この男はあえてハックを書き、それをトランクに送信したので、大丈夫です。 特に「理解」している場合に、MinGWが弱いWindowsのようなハックselectanyを使用できるという歴史はありません。 ただし、彼は上記のエラーからも保存しません。
そして何をすべきか? そして何をすべきか?
私の経験からわかるように、マジック定数は通常、ブロックまたはプログラムモジュールに固有です。 グローバル名前空間をそれらで詰まらせることは十分にまれなので、誰もあなたが書くことを禁じることはありません:
Myclass.h
class MyClass { /* ... */ static constexpr float m_pi {3.14}; /* ... */ };
Myclass.cpp
constexpr float MyClass::m_pi;
これにより、定数の可読性と可視性および禁止された伝播の問題が完全に解決されること。 このリンクでは、はい、質問は開いたままですが、これは何よりも多くのことです(特に標準の実装を考慮)。 盗作の非難を避けるために、 ここにリンクを追加します。ここでも同様のことが説明されています。
ただし、上記は非常に小さな問題の深刻なループであるように思われませんか? 現実には、誰もが理解しています:
- piのような定数はヘッダーにあります(多くの場合、マイクロコントローラーにもあります)。
- マジック定数が必要な場合、小数より整数であることが多いため、コンパイラによって最適化される可能性が最も高いことを意味します(far-fetched、yes)。
- 一定で大きなものの場合、余分な行を数行書くことはそれほど不幸ではありません(たとえば、必要に応じてクラスまたは外部で静的constを使用しました)。
- 同じ値であるが、異なる名前(エイリアス)を持ち、弱いものがない定数は、場合によっては最適化できます。