ここに私のコードがあります:
_class test{
public:
constexpr test(){
}
constexpr int operator+(const test& rhs){
return 1;
}
};
int main(){
test t; //constexpr Word isn't necessary
constexpr int b = t+test(); // works at compile time!
int w = 10; // ERROR constexpr required
constexpr int c = w + 2; // Requires w to be constexpr
return 0;
}
_
Testをconstexpr
に指定していなくても機能していることに気付きました。 int
でも同じことを行って結果を複製しようとしましたが、エラーが発生しました。具体的には、_int w
_内の_constexpr int c = w + 2;
_をconstexpr
にしたいのです。 test
を使用している私の最初の試みから、すでにコンストラクタでconstexpr
を使用しているという理由で機能しましたか?その場合、コンストラクターにconstexpr
を持つすべてのクラスは、constexpr
になるようにインスタンス化または作成されたすべてのオブジェクトに帰着すると仮定するとよいでしょうか?
ボーナス質問:
constexpr
コンストラクタがある場合、次のようなことをするのは悪いですか? test * t = new test();
?
Constexprコンストラクターを使用しても、その変数の宣言は自動的にconstexprにならないため、t
はconstexprではありません。この場合に起こっているのは、次の行のconstexpr関数を呼び出していることです。
_constexpr int b = t+test();
_
次のように表示できます。
_constexpr int b = t.operator+( test() );
_
したがって、問題はtest()
が定数式であるかどうかです。これは、コンストラクターがconstexprであり、ドラフトC++ 11標準セクション_5.19
_[expr.const]パラグラフ_2
_:
条件式は、潜在的に評価される部分式として次のいずれかを含まない限り、コア定数式です[...]
次の箇条書きが含まれています。
- リテラルクラスのconstexprコンストラクターまたはconstexpr関数以外の関数の呼び出し[注:オーバーロード解決(13.3)は、通常どおり適用されます—エンドノート]。
[...]
関数呼び出し置換(7.1.5)で置換されたときに、mem-initializersのコンストラクター呼び出しおよび完全式のすべての定数式を生成しない引数を持つconstexprコンストラクターの呼び出し
実装定義の再帰制限を超えるconstexpr関数またはconstexprコンストラクターの呼び出し(付録Bを参照)。
これは、メンバー変数test
を導入してx
にいくつかの小さな変更を加えることで、より簡単に確認できます。
_class test{
public:
constexpr test(){
}
constexpr int operator+(const test& rhs) const {
return x + 1 ;
}
int x = 10 ;
};
_
_operator +
_でアクセスしようとすると、次の行が失敗することがわかります。
_constexpr int b = t+test();
_
clangからの次のエラー(実際に見る):
_error: constexpr variable 'b' must be initialized by a constant expression
constexpr int b = t+test(); // works at compile time!
^ ~~~~~~~~
note: read of non-constexpr variable 't' is not allowed in a constant expression
return x + 1 ;
^
_
t
はconstexpr変数ではないため、失敗します。したがって、そのサブオブジェクトもconstexpr変数ではありません。
2番目の例:
_ constexpr int c = w + 2;
_
ドラフトC++ 11標準セクション_5.19
_[expr.const]の例外の1つに該当するため、機能しません。
左辺値から右辺値への変換(4.1)
[...]
- 整数型または列挙型のglvalue。先行する初期化を持つ不揮発性constオブジェクトを参照する、定数式で初期化される、または
constexpr
コンストラクターがクラス型に与える影響は、C++標準で読み取ることができます。
3.9タイプ
(...)
次の場合、型はリテラル型です。
- 集約型(8.5.1)であるか、少なくとも1つのconstexprコンストラクターまたはコピーまたは移動コンストラクターではないコンストラクターテンプレートを持っている
(...)
constexpr
コンストラクターは、静的初期化を実行でき、 this one は可能です:
#include <iostream>
struct test {
int val;
constexpr test(int val) : val(val) { }
};
template<int N>
struct CC {
double m[N];
};
int main()
{
CC<test(6).val> k; // usage where compile time constant is required
std::cout << std::end(k.m) - std::begin(k.m) << std::endl;
return 0;
}
test
がリテラルクラスであるという単なる事実は、すべてのインスタンスが定数式:
#include <iostream>
struct test {
int val;
constexpr test(int val) : val(val) { }
};
int main()
{
test a(1);
++a.val;
std::cout << a.val << std::endl;
return 0;
}
上記の例では、インスタンスa
は定数として宣言されていなかったため、a
がconstexpr
定数であっても、それは1つではありません(変更できるため)。
私の実験によるconstexprキーWord この回答では 多かれ少なかれ、その呼び出しで指定されたすべてのコードパスを静的に解決できる必要があることをコンパイラに指示します。つまり、少なくとも現時点では(表示されます)、すべてのコードパスに沿ってconstexprを宣言する必要があります。そうしないと失敗します。たとえば、コードでは、演算子またはコンストラクタconstexprを宣言しないと、bへの初期constexprの割り当ては失敗します。 constexprは、constexprとして宣言されている変数に割り当てた場合にのみ有効になるようです。 constexpr変数の割り当てで明示的に指示しない場合。
そうは言っても、コンストラクタconstexprを宣言しても、通常の状況では影響はないようです。以下のマシンコードは、次のコマンドラインで作成されました。
g++ -std=c++11 -Wall -g -c main.cpp -o obj/Debug/main.o
g++ -o bin/Debug/TestProject obj/Debug/main.o
そのため、bの割り当てにより次のコードが生成されます。
0x4005bd Push rbp
0x4005be mov rbp,rsp
0x4005c1 mov DWORD PTR [rbp-0x4],0x1
0x4005c8 mov eax,0x0
0x4005cd pop rbp
0x4005ce ret
ただし、b変数のconstexpr宣言を削除する場合:
0x4005bd Push rbp
0x4005be mov rbp,rsp
0x4005c1 sub rsp,0x10
0x4005c5 lea rax,[rbp-0x5]
0x4005c9 mov rdi,rax
0x4005cc call 0x4005ee <test::test()>
0x4005d1 lea rdx,[rbp-0x5]
0x4005d5 lea rax,[rbp-0x6]
0x4005d9 mov rsi,rdx
0x4005dc mov rdi,rax
0x4005df call 0x4005f8 <test::operator+(test const&) const>
0x4005e4 mov DWORD PTR [rbp-0x4],eax
0x4005e7 mov eax,0x0
0x4005ec leave
0x4005ed ret
演算子とコンストラクタがconstexprとして宣言されていないように処理されているように見えますが、実際には、コンパイラに関する詳細を確認する必要があります。