2017-10-07 14 views
1

は現在、私はスコット・マイヤーズ効果的な近代的なC++( - 使用constexprの可能な限り。項目15)を読んでいます。著者は言う:引数がコンパイル時に知られていないとき、 `constexpr`コンストラクタが呼び出されない

constexprの機能が コンパイル時に知られていない、それは は、実行時にその結果を計算し、通常の関数のように作用する1つ以上の値で呼び出された場合。これは、コンパイル時には の定数と他のすべての値に対して1つ、同じ操作を実行するために2つの 関数を必要としないことを意味します。 constexpr関数はそれを allで行います。

は、私はすべてが期待通りに行くp1オブジェクトを作成する場合は

#include <iostream> 

class Point 
{ 
    public: 
     constexpr Point(double a, double b) noexcept 
      : _a(a), _b(b) 
     { 
     } 

     void print() const noexcept 
     { 
      std::cout << "a -> " << _a << "\tb -> " << _b << std::endl; 
     } 

    private: 
     double _a; 
     double _b; 
}; 

double get_a() noexcept 
{ 
    return 5.5; 
} 

double get_b() noexcept 
{ 
    return 5.6; 
} 


int main() 
{ 
    constexpr Point p1(2.3, 4.4); 
    p1.print(); 
    int a = get_a(); 
    int b = get_b(); 
    constexpr Point p2(a, b); 
    p2.print(); 
    return 0; 
} 

http://coliru.stacked-crooked.com/に次のコードスニペットを試してみた:引数はコンパイル時に知られており、メンバーが正しく初期化されています。 p2オブジェクトを作成する場合、コンパイル時に変数abの値はわかりませんが、コンストラクタが通常の関数として動作している必要があるため、私の理解のもとになっているはずです。しかし、私は次のエラーメッセージを取得しています:

main.cpp: In function 'int main()' 
main.cpp:38:28: error: the value of 'a' is not usable in a constant expression 
    constexpr Point p2(a, b); 
          ^
main.cpp:36:9: note: 'int a' is not const 
    int a = get_a(); 
     ^
main.cpp:38:28: error: the value of 'b' is not usable in a constant expression 
    constexpr Point p2(a, b); 
          ^
main.cpp:37:9: note: 'int b' is not const 
    int b = get_b(); 

ColiruGCCコンパイラを使用しています。 問題は何か分かりません。たぶん私はcppreference(強調鉱山)から何か...

+1

それのであなたは、 'constexpr''としてp2'を宣言するべきではありませんすることはできません。だから、ちょうど 'ポイントp2(a、b);はうまくいくでしょう。 – songyuanyao

+0

なぜですか? 'p1'では、コンパイル時に既知の引数:2.3、4.4があるので、コンパイル時に初期化されます。 'p2'の場合、' a'と 'b'のコンパイル時の値は分かりません。メンバが初期化されているはずです。 (コンパイル時に知られていない値を持つconstexpr関数が呼び出されると、通常の関数のように動作し、実行時に結果が計算されます)。 –

+1

あなたの見積もりは関数を参照します。オブジェクトまたは式 'constexpr'を宣言すると、コンパイル時に評価されることが必要になります。これは、' p2'はソングゥワニが指摘するようにはできません。 – patatahooligan

答えて

6

を見逃している:

constexpr変数は、次の要件を満たしている必要があります

  • そのタイプはLiteralTypeでなければなりません。
  • が、それはすぐにすべての暗黙の変換、コンストラクタの呼び出しなどを含めた初期の
  • フル表現を、初期化しなければならない、定数式、あなたの例では

でなければなりません...

constexpr Point p2(a, b); 

... ab定数式ではありません。それらの定数式にするために、あなたはconstexprとしてget_aget_ba、およびbをマークする必要があります。

constexpr double get_a() noexcept 
{ 
    return 5.5; 
} 

constexpr double get_b() noexcept 
{ 
    return 5.6; 
} 

constexpr int a = get_a(); 
constexpr int b = get_b(); 
constexpr Point p2(a, b); 
4

あなたがスコットの説明を誤解:彼はあなたが作ることができることを意味するものではありませんでしたconstexpr非constデータを持つオブジェクト。コンパイラはabの値を知らないので、あなたがあなたのp2オブジェクトがconstexprでコンパイラに宣言することはできませんので、このような構築物は

constexpr Point p2(a, b); 

を動作するようになっていません。彼は何を意味

は、あなたがこの

int constexpr foo(int a, int b) { 
    return 2*a + b; 
} 

のように、constexpr機能やメンバ関数を定義するときabは、コンパイル時定数をしているとき、それは明らかに動作しますが、それはときにも作業を続けることでしたabである変数:

cout << foo(2, 5) << endl; // This obviously works 
int a, b; 
cin >> a >> b; 
cout << foo(a, b) << endl; // This works too 

Demo.

あなたのケースでは、それはあなたがさえ変数をPointのコンストラクタを呼び出し続けることができますが、あなたはconstexprに結果を強制することはできませんことを意味します

Point p2(a, b); // This works, even though Point(int,int) is constexpr 
+0

私の場合、 'a'と' b'は変数ではありませんか? –

+0

実際問題は、私が 'constexpr'として' p2'を宣言したことでした。 –

+1

@DavidHovsepyanもちろん、そうです。つまり、それらを 'constexpr'関数に渡すことができますが、関数の結果が' constexpr'でもあることを意味するわけではありません。 'constexpr'を' p2'に強制することはできません。これは変数で呼び出された 'constexpr'関数の結果です。 – dasblinkenlight

関連する問題