web-dev-qa-db-ja.com

コンストラクタでconstexprを指定すると、それから作成されたすべてのオブジェクトが自動的にconstexprになりますか?

ここに私のコードがあります:

_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();

35

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オブジェクトを参照する、定数式で初期化される、または
19
Shafik Yaghmour

constexprコンストラクターがクラス型に与える影響は、C++標準で読み取ることができます。

3.9タイプ

(...)

  1. 次の場合、型はリテラル型です。

    • 集約型(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;
}

Demo

上記の例では、インスタンスaは定数として宣言されていなかったため、aconstexpr定数であっても、それは1つではありません(変更できるため)。

10

私の実験による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として宣言されていないように処理されているように見えますが、実際には、コンパイラに関する詳細を確認する必要があります。

3
Ragora