7

次のテストコードがあります。

#include <cstdint>
#include <cassert>

enum class Result : std::uint32_t {SUCCESS = 0, INSUCCESS = 1};

void* func(Result& result)
{
    // works great
    /*
    result = Result::INSUCCESS;
    return NULL;
    */

    // error: invalid conversion from ‘long int’ to ‘void*’ [-fpermissive]
    /*
    return result = Result::INSUCCESS, NULL;
    */

    // compiles, but <result> is not set???
    return result = Result::INSUCCESS, nullptr;
}

void testReturnWithSideEffects()
{
    Result result = Result::SUCCESS;
    func(result);
    assert(result == Result::INSUCCESS);
}

ここには2つの質問がありますが、私は主に2番目に興味があります:

結果が設定されないのはなぜですか?

編集:これを確認してくれてありがとう。私が使用することにした回避策は、次のものを置き換えることです。

return result = Result::INSUCCESS, nullptr;

次のように:

return result = Result::INSUCCESS, (void*)NULL;

追記: もちろん、私の実稼働シナリオは別のポインター型 (void* ではない) を使用していますが、説明のために単純化しました。

別の注意: 回避策から、その nullptr で怪しいことが起こっていることがわかります。コンパイルされないサンプル行は実際にコンパイルされるはずであり、これら2つの問題はおそらく何らかの形で関連していると思います。

そして、私のコードの「トリッキー」または「読みにくい」を概説した人への 3 番目の最後の注意: 読みやすさは主に主観的な問題です。スポット欠陥。

4

3 に答える 3

12

2 番目のケース:

return result = Result::INSUCCESS, nullptr;

returngcc のバグのように見えます。ステートメントをこれに変更すると、return ステートメントのコンテキストでコンマ演算子の左側が無視されているようです(ライブを参照してください)。

return (printf("hello\n"), nullptr);

出力はありませんが、returnステートメントの外でこれを行うと、期待される結果が得られます。で修正されているように見えますが、と で4.8再現できます。4.64.7

ドラフト C++ 標準セクションの5.18 カンマ演算子を見ると、次のように書かれています (強調鉱山):

コンマで区切られた式のペアは、左から右に評価されます。左の式は破棄された値の式です (箇条 5)。83 左の式に関連付けられたすべての値の計算と副作用は、右の式に関連付けられたすべての値の計算と副作用の前に並べられます。結果の型と値は、右側のオペランドの型と値です。結果は、右オペランドと同じ値カテゴリであり、右オペランドが glvalue とビット フィールドの場合はビット フィールドです。右側のオペランドの値が一時的 (12.2) である場合、結果はその一時的です。

いずれにせよ、このコードは巧妙だが読みにくいため保守が難しいと何人かが既に述べているように、これを 2 行に分割すると問題なく動作します。私はいつも、Brian Kernighan からのこの引用を心に留めておきたいと思っています(これにはいくつかの他のバージョンがあるかもしれません):

デバッグは、そもそもプログラムを書くよりも 2 倍難しいことは誰もが知っています。それで、あなたがそれを書くときにできる限り賢いなら、どうやってそれをデバッグするのでしょうか?

4.10 最初のケースでは、セクションPointer conversionsを見ると、エラーは有効です( employment mine going forward ):

null ポインター定数は、0 に評価される整数型の整数定数式 (5.19) prvalue または 型 std::nullptr_t の prvalueです。null ポインター定数はポインター型に変換できます。結果はその型の null ポインター値であり、オブジェクト ポインターまたは関数ポインター型の他のすべての値と区別できます。このような変換をヌルポインタ変換と呼びます。

表現:

result = Result::INSUCCESS, NULL

が含まれているため、定数式ではありません。定数式とは=、セクション5.19 定数式で説明されており、次のように述べています。

条件式は、潜在的に評価される部分式として次のいずれかを含まない限り、コア定数式です (3.2) [...]

以下が含まれます。

割り当てまたは複合割り当て(5.17); また

nullptrstd::nullptr_tの prvalue であるため、使用しても問題ありません。セクション12.14.7 Pointerlitersから次のことがわかります。

ポインタ リテラルはキーワード nullptr です。タイプ std::nullptr_t の prvalueです。[ 注: std::nullptr_t は、ポインター型でもメンバー型へのポインターでもない特殊な型です。むしろ、この型の prvalue はヌル ポインター定数であり、ヌル ポインター値またはヌル メンバー ポインター値に変換できます。4.10 と 4.11 を参照してください。—文末脚注]

于 2014-05-09T12:30:06.727 に答える