9

安全な bool イディオムに関連する別の質問があります。

typedef void (Testable::*bool_type)() const;             // const necessary?
void this_type_does_not_support_comparisons() const {}   // const necessary?

operator bool_type() const
{
    return ok_ ? &Testable::this_type_does_not_support_comparisons : 0;
}

bool_type(typedef) とthis_type_does_not_support_comparisonsはどうしてconstですか? とにかく、誰も実際に戻りポインターを介してメンバー関数を呼び出すことは想定されていませんよね? ここはconst必要ですか?operator bool_typeそうでなければ、(メンバー関数は) const-correctness に違反しますか?

4

2 に答える 2

5

「セーフブールイディオム」は、「スポーツカーとトラクターの両方、そしておそらくボートである車両が欲しい」という質問に対する技術的な答えです. 実用的な答えは技術的な答えではありません…

とは言っても、それが解決する問題は、変換可能な結果を​​与えるだけですが、他のものには変換boolできません (そうでなければ、クラスのインスタンスを実引数として渡すことができます。たとえば、仮引数は でしたint)。データ ポインタは に変換できますvoid*。関数ポインターは、少なくとも正式には C++ 標準内ではありません (Posix は別のものであり、実践もしています)。

メンバー関数ポインターを使用すると、安全な bool 演算子からのポインターを指定して、誤って関数を呼び出すことを防ぎます。constそれを少し制限しますが、運命が愚かな間違いの最大数を犯す道に誰かを置いた場合、その人はまだ何もしない関数を呼び出すことができるかもしれません. の代わりにconst他のコードがそのような引数を提供できない場合、プライベート型の引数を持たせるだけでよいと思います。そうすれば、愚かなメンバー関数型である必要はもうありません。

次のようになります。

#include <stdio.h>

class Foo
{
private:
    enum PrivateArg {};
    typedef void (*SafeBool)( PrivateArg );
    static void safeTrue( PrivateArg ) {}

    bool    state_;

public:
    Foo( bool state ): state_( state ) {}

    operator SafeBool () const
    { return (state_? &safeTrue : 0); }
};

int main()
{
    if( Foo( true ) ) { printf( "true\n" ); }
    if( Foo( false ) ) { printf( "false\n" ); } // No output.

    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}

もちろん、実際の答えは次のようなものです。

#include <stdio.h>

class Foo
{
private:
    bool    isEmpty_;

public:
    Foo( bool asInitiallyEmpty )
        : isEmpty_( asInitiallyEmpty )
    {}

    bool isEmpty() const { return isEmpty_; }
};

int main()
{
    if( Foo( true ).isEmpty() ) { printf( "true\n" ); }
    if( Foo( false ).isEmpty() ) { printf( "false\n" ); } // No output.

    //bool const x0 = Foo( false );       // No compilado!
    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}

要約 尋ねられた質問:

  • bool_type (typedef) と this_type_does_not_support_comparisons はなぜ const なのですか?

誰かが自分のコードを完全には理解していませんでした。あるいは、通話機能を少し制限するつもりだったのかもしれません。しかし、それでは、かなり無駄な手段です。

  • とにかく、誰も実際に戻りポインターを介してメンバー関数を呼び出すことは想定されていませんよね?

右。

  • ここでconstは必要ですか?

いいえ。

  • そうでなければ、演算子 bool_type (メンバー関数) は const-correctness に違反しますか?

いいえ。

乾杯 & hth.,

于 2011-08-11T15:36:13.527 に答える
1

8.3.5 / cv-qualifier-seqは、非静的メンバー関数の関数型、メンバーへのポインターが参照する関数型、または関数typedef宣言の最上位関数型の一部である必要があります。関数宣言子でのcv-qualifier-seqの効果は、関数型の上にcv-qualificationを追加することと同じではありません。つまり、cv-qualified関数型を作成しません。

正しく読めば、constメンバー関数で非constメンバーへのポインターを返すことができます。const以外のオブジェクトで呼び出すことはできません。

呼び出しを禁止する方法は次のとおりです。

private:
    struct private_ 
    {
        void this_type_does_not_support_comparisons() {}
    };

public:
    typedef void (private_::*bool_type)() const;

    operator bool_type() const
    {
        return ok_ ? &private_::this_type_does_not_support_comparisons : 0;
    }

メンバー関数へのポインターは、同等かどうかを比較できます。エラーをトリガーするタイプのoperator==operator!=を記述する必要があります。Testable::bool_typeこれらの演算子はテンプレートになり、誤った本体を持つ可能性があるため、安全なブールイディオムのCRTP形式を使用する方が簡単です。

例:

template <typename T>
class safe_bool_concept
{
    // Implementation detail of safe bool
protected:
    ~safe_bool_concept() {}

public:
    operator safe_bool() const
    {
        return static_cast<const T*>(this)->is_null() ? ...;
    }
};

struct Foo : safe_bool_concept<Foo>
{
    ...

private:
    friend class safe_bool_concept<Foo>;
    bool is_null() const { ... }
};

次に、次のことができます(で同じことを行います!=):

template <typename T>
void operator==(const safe_bool_concept<T>& x, const safe_bool_concept<T>&)
{
    x.some_private_member(); // invalid, but won't be generated
                             // unless safe_bool classes are compared
}

これは、比較を禁止したい場合は、CRTPを介して安全なブールイディオムを実装する必要があることを意味します。ただし、ゼロとの比較は引き続き機能します。

非メンバー関数ルートを使用する場合は、、、も指定する必要が<あります。><=>=

于 2011-08-11T15:33:16.923 に答える