constexpr
関数の1つの側面でC ++ 11と後方互換性がありません。
C ++ 11では、
constexpr
メンバー関数を定義すると、暗黙的に
const
指定子が取得されます。
// C++11 struct NonNegative { int i; constexpr int const& get() /*const*/ { return i; } int& get() { return i; } };
get
関数の最初の宣言は、明示的に指定しなくても
const
指定子を受け取ります。 したがって、これら2つの関数は、
const
バージョンと非
const
バージョンです。
C ++ 14では、これは当てはまりません。両方の宣言が、異なる戻り値を持つメンバー関数の同じ非
const
バージョンを決定するため、コンパイルエラーが発生します。
constexpr
関数の使用を既に開始していて、暗黙的な
const
指定子を希望している場合は、C ++ 14コンパイラーに切り替えることを決定した場合にコードがコンパイルし続けるように、明示的に追加することをお勧めします。
暗黙のconst
何が問題になっていますか?
次のようにタイプを使用しようとすると、問題が始まります。
// C++11 constexpr int i = NonNegative{2}.get(); // ERROR
(やや珍しい)C ++ルールによれば、一時オブジェクトのメンバー関数を選択するときは、
const
バージョンよりも
const
バージョンの方が適しています。 非
const
メンバー関数
get
constexpr
で
get
ないため、
constexpr
変数の初期化には使用できず、コンパイル段階でエラーが発生します。 この関数を
constexpr
にすることはできません
constexpr
が自動的に追加されるためです...
最適な関数を選択するためのルールは、一時オブジェクトに最適な非メンバー関数を選択する方法に少し反するため、珍しいと言いました。 この場合、非
const
バージョンへのリンクの
const
左辺値を優先します。
// C++11 constexpr int const& get(NonNegative const& n) { return ni; } constexpr int& get(NonNegative& n) { return ni; } NonNegative N = readValue(); constexpr int * P = &get(N); int main() { *P = 1; }
何が起こるか見てください:グローバル変数
N
定数で
N
ません。 したがって、2番目の非
const
オーバーロード関数は、ポインター
P
初期化時に呼び出されるように選択されます
P
しかし、非
const
関数には
constexpr
がまだあり
constexpr
! そして、すべてのルール「
constexpr
は
const
意味する」は、非静的メンバー関数の暗黙の
this
引数にのみ適用されるためです。
constexpr
関数は、非
const
オブジェクトへの参照を取得し、非
const
サブオブジェクトへのリンクを返すことができます。 ここには問題はありません。グローバルオブジェクトのアドレスは一定であり、コンパイル時に既知です。 ただし、アドレス
P
の値は一定で
P
なく、後で変更できます。
前の例がやや難解に見える場合は、次のより現実的な例を検討してください。
// C++11 constexpr NonNegative* address(NonNegative& n) { return &n; } NonNegative n{0}; // non-const constexpr NonNegative* p = address(n);
ここではすべて正常に機能しますが、
address
をメンバー関数にしようとすると、機能しなくなります。
// C++11 struct NonNegative { // ... constexpr NonNegative* maddress() { return this; } // ERROR }; NonNegative n{0}; // non-const constexpr NonNegative* p = n.maddress();
これは、
maddress
const
指定
maddress
暗黙的に定義され、
this
が
NonNegative const*
型であり、
NonNegative*
変換できないためです。
これはメンバー関数自体が
const
であるのではなく、関数に対する暗黙の (
this
)引数であることに注意してください。 メンバー関数宣言は、次のように擬似コードで書き直すことができます。
// PSEUDO CODE struct NonNegative { // ... constexpr NonNegative* maddress(NonNegative const& (*this)); };
そして、この暗黙的な関数引数は、他の関数引数とは異なり、(時には望ましくない)
const
修飾子を取得します。
この非対称性はC ++ 14で削除されます。 暗黙的な引数(
this
)の
const
指定子が必要な場合は、自分で追加する必要があります。 次のコードは、C ++ 14で有効です。
// C++14 struct NonNegative { int i; constexpr int const& get() const { return i; } constexpr int& get() { return i; } };