【问题标题】:What is the non-pointer equivalent of NULL?NULL 的非指针等价物是什么?
【发布时间】:2012-06-07 10:12:36
【问题描述】:

为我的不当术语道歉。

如果条目不存在,我有一段代码返回 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

如何对返回的对象执行等效检查?

【问题讨论】:

  • boost::optional<ObjectType> 还是例外。

标签: c++ null


【解决方案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";
}

【讨论】:

  • 感谢您的回答。你能演示一下这是怎么做到的吗?
【解决方案2】:

以下代码没有报错,因为标准非常保守。

有些代码结构极其复杂,编译器无法知道是否可以到达函数的末尾。因此标准规定编译器不必证明函数正确返回值...

然而标准确实说如果一个函数正常结束(没有例外)而没有返回一个值,那么 Undefined Behavior 被调用(即,任何事情都可能发生,可能是崩溃)。因此,大多数编译器都会针对这种情况发出警告,对于 gcc 和 Clang,您可以使用 -Wreturn


现在,nullity 或 sentinel values 的原则并不新鲜,空指针只是 一个 的化身(在众多中)。

如果您的对象可以为空没有意义(它很少这样做,但可能是一种权宜之计),那么您有两种选择:

  • throw 异常提示错误
  • 返回一个可能为空的包装类(如boost::optional&lt;ObjectType&gt;

在这种情况下,由于预计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;
}

【讨论】:

  • boost::none 可以转换成false 吗?
  • @Nawaz:我不这么认为......而且我不明白你为什么要问。
  • @Nawaz: boost::optional&lt;T&gt; 可用于布尔表达式,依赖于safe bool idiom 的变体。所以你可以写if (myOptional)来测试值的存在(或者if (!myOptional)来测试它的不存在)。
  • @Nawaz: 另一方面,boost::noneboost::none_t 的一个实例,并且只是用作标签来构造optional 的一个单元化实例。没有理由将none_t 转换为bool,实际上它可能只是一个空结构。编辑:嗯,它实际上是a bit more complicated,而不是一个简单的空结构(不过我不知道为什么)。
  • @Nawaz: 啊抱歉,boost::none 的类型是 boost::none_tboost::optional&lt;T&gt; 有几个构造函数(通常依赖于 T)加上一个构造函数 optional(none_t),它只是将它初始化为“空”状态。
【解决方案3】:

C++17 更新

C++17 引入了std::optional 作为标准库的一部分,这是语言中最接近空对象的东西,类似于其他编程语言中的“可能”。它的工作原理很像the previous answer 中描述的boost::optional。它旨在解决的用例之一是从函数返回一个可选值。

#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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 2011-07-19
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 2013-08-10
    • 2014-06-12
    相关资源
    最近更新 更多