13

私の不適切な用語についてお詫び申し上げます。

エントリが存在しない場合に NULL ポインターを返すコードがあります。

ObjectType * MyClass::FindObjectType( const char * objectTypeName )
{
    if ( objectTypeMap.find( objectTypeName ) == objectTypeMap.end() )
    {
        Msg( "\n[C++ ERROR] No object type: %s", objectTypeName );
        return NULL;
    }
    else
        return &objectTypeMap[ objectTypeName ];
}

同じことをしたいのですが、今回は単なるポインターではなくオブジェクトを返します。次のコードでは、コンパイラ エラーは発生しません (これには驚きました)。

ObjectType MyClass::FindObjectType( const char * objectTypeName )
{
    if ( objectTypeMap.find( objectTypeName ) == objectTypeMap.end() )
    {
        Msg( "\n[C++ ERROR] No object type: %s", objectTypeName );
    }
    else
        return objectTypeMap[ objectTypeName ];
}

ポインターを使用すると、次のようにエントリが見つからなかったかどうかを確認できます。

if ( FindObjectType( objectType ) == NULL )
    //Do something

返されるオブジェクトで同等のチェックを実行するにはどうすればよいですか?

4

3 に答える 3

12

オブジェクトに相当する言語レベルはありません。

1 つのオプションは、「実際の」オブジェクトと比較して等しくないことが保証されている「センチネル」オブジェクトを作成し、それを返すことです。

class ObjectType {
public:
    static const ObjectType null;

    bool operator==(const ObjectType &rhs) const { /* need an appropriate comparison test */ }

    ...
};

ObjectType ObjectType::null(/* something unique */);


...

ObjectType foo(const char *objectTypeName) {
    if (cond) {
        return objectTypeMap[objectTypeName];
    } else {
        return ObjectType::null;
    }
}


...

if (foo(objectType) == ObjectType::null) {
    std::cout << "Returned the null object\n";
}
于 2012-06-07T10:13:51.470 に答える
4

標準は非常に保守的であるため、次のコードはエラーを出していません。

一部のコード構造は非常に複雑であり、コンパイラは関数の終わりに到達するかどうかを判断できません。したがって、標準では、関数が値を正しく返すことをコンパイラが証明する必要はないと述べています...

ただし、標準では、値を返さずに関数が正常に終了した場合 (例外なし)、未定義の動作が呼び出される (つまり、クラッシュなど、あらゆることが起こり得る) と規定されています。したがって、ほとんどのコンパイラには、このような状況に対する警告があります。gcc と Clang では、-Wreturn.


現在、ヌル値またはセンチネル値の原則は新しいものではなく、ヌル ポインターは(多くの中で) 1 つの具体化にすぎません。

オブジェクトが null 可能であることが意味をなさない場合 (めったにありませんが、便利な場合があります)、次の 2 つの選択肢があります。

  • throwエラーを通知する例外
  • boost::optional<ObjectType>null の可能性があるラッパー クラス ( など) を返す

この状態ではFind何も見つからないことが予想されるため、一般的には後者をお勧めします。

使い方は簡単です:

boost::optional<ObjectType> MyClass::FindObjectType(char const* objectTypeName )
{
    if ( objectTypeMap.find( objectTypeName ) == objectTypeMap.end() ) {
        // do not print anything, it is up to the caller to decide what to do
        return boost::none;
    }

    return objectTypeMap[ objectTypeName ];
}

そして、呼び出し元は次のように書きます。

int main(int argc, char* argv[]) {
    if (boost::optional<ObjectType> o = MyClass::FindObject(argv[1])) {
        o->foo();

        return 0;
    }

    Msg( "\n[C++ ERROR] No object type: %s", argv[1]);
    return 1;
}
于 2012-06-07T11:49:50.697 に答える
2

C++17 アップデート

C++17std::optionalは、標準ライブラリの一部として、言語の null オブジェクトに最も近いものを導入します。これは、他のプログラミング言語の "Maybe" に似ています。前の回答boost::optionalで説明したのと同じように機能します。解決しようとしている使用例の 1 つは、関数からオプションの値を返すことです。

#include <iostream>
#include <optional> // Defines std::optional

std::optional<int> f(bool test) {
    if (test) {
        return 1;
    } else {
        return std::optional<int>();
    }
}

int main()
{
    auto ret = f(true);
    
    // std::optional can be used as a bool
    if (ret) {
        std::cout << "Value for first test: " << ret.value() << '\n';
    } else {
        std::cout << "Value for first test: " << 0 << '\n';
    }
    
    std::cout << "Value for second test: " << (f(true)).value_or(0) << '\n';
    std::cout << "Value for third test: " << (f(false)).value_or(0) << '\n';
    
    return 0;
}

出力:

Value for first test: 1
Value for second test: 1
Value for third test: 0
于 2020-08-26T06:27:32.250 に答える