2012-10-23 13 views
15

通常、constexprには副作用がない必要があります。しかし、私は、スローされた例外のコンストラクタで副作用を使用することが可能であることを発見しました。このテクニックは、以下のプログラムで実証されているように、constexpr関数のassert()をエミュレートするために使用できます。constexprによってスローされた例外に副作用を使用することは合法ですか?

#include <iostream> 
#include <cstdlib> 
#include <stdexcept> 

struct constexpr_precond_violated : std::logic_error 
{ 
    constexpr_precond_violated(const char* msg) : 
    std::logic_error(msg) 
    { 
    std::cerr << msg << '\n'; 
    abort(); // to get a core dump 
    } 
}; 

#define TO_STRING_IMPL(x) #x 
#define TO_STRING(x) TO_STRING_IMPL(x) 

#define CONSTEXPR_PRECOND(cond, value) \ 
    ((!(cond)) ? throw constexpr_precond_violated(\ 
    "assertion: <" #cond "> failed (file: " \ 
    __FILE__ ", line: " TO_STRING(__LINE__) ")") \ 
    : (value)) 

constexpr int divide(int x, int y) 
{ 
    return CONSTEXPR_PRECOND(y != 0, x/y); 
} 

int main(int argc, char** argv) 
{ 
    // The compiler cannot know argc, so it must be evaluated at runtime. 
    // If argc is 2, the precondition is violated. 
    return divide(100, argc - 2); 
} 

g ++ 4.7.2とclang ++ 3.1でテストしました。前提条件が失敗すると、エラーの場所とコアダンプが表示されます。

./constexpr_assert some_arg 
assertion: <y != 0> failed (file: constexpr_assert.cpp, line: 26) 
Aborted (core dumped) 

したがって、現在のコンパイラで動作しますが、正当なC++ 11ですか?

+1

第2引数として 'divide()'にコンパイル時定数 '0'を指定するとどうなりますか? *コンパイラが "例外をスロー"していますか? :) –

+0

static_assert(除算(1,0)> = 0、 "テスト"); は単にリターン除算(1、0)をコンパイルしないであろう。 はClangでコンパイルされ、実行時にのみ失敗します。 –

+3

定数式と 'constexpr'関数を区別するように注意する必要があります。 "' 'constexpr'のようなものは副作用のないものでなければなりません"というのは不正確です。 'constexpr'をキーワードにすぎないものと考えてください(' static'が気になります)。 –

答えて

14

これは合法です。

constexpr機能について定数式(§7.1.5/ 5)につながるいくつかの引数値が存在しなければならない。

constexpr機能のために、何の関数の引数の値は、このような が存在しない場合関数呼出し置換は定数 式(5.19)を生成し、プログラムは不正な形式になります。診断は必要ありません。

これはあらゆる可能引数の値が一定の発現をもたらす必要があることを意味するわけではないことに注意してください。 divideには、定数式になる引数値が明示的に含まれています。divide(1, 1)は簡単な例です。したがって、その定義は明らかに有効です。

でも、divide(1, 0)と呼ぶことができますか?はい、できます。 等価非constexpr

constexpr関数の呼び出しが呼び出しと同じ結果を生成する:constexpr機能又は「正常な」関数(§7.1.5/ 7)呼び出しの間にほとんど差はありません関数 を呼び出してconstexpr関数を呼び出すことは、定数式で表すことができます。 constexpr機能に呼び出す

は定数式で表示されますが、何も定数式が得られないからそれらを禁止していません。これは、コンパイル時と実行時の両方の引数を持つ関数constexprを呼び出すことができるように意図されています(それ以外の場合は、constexprの有用性は制限されます)。完全を期すため

、のは、定数式(§5.19/ 2)になるか見てみましょう:

それ は、として次のいずれかを含む場合を除き条件式コア定数式あります評価されない論理AND(§5.14)、論理OR(§5.15)、 、および条件(§5.16)の部分式は、 と見なされます[...]。

したがって、divide(1, 1)は定数式ですが、divide(1, 0)はありません。テンプレートパラメータにdivide(1, 0)を使用した場合、プログラムは不正な形になります。しかしそれ以外の場合は問題ありません。

関連する問題