Constexpr関数にはconst指定子がありません

警告したいだけです。C++ 14は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 constexprgetないため、 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 ! そして、すべてのルール「 constexprconst意味する」は、非静的メンバー関数の暗黙の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暗黙的に定義され、 thisNonNegative 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; } }; 

Source: https://habr.com/ru/post/J184250/


All Articles