消息关闭
    暂无新消息!

nullptr与NULL的转换?

问题作者 : 黎妹2017-08-15发布

语言限定: c++(非c)

namespace czxyl
{
    char* my_strcpy(char* dest, const char *src)
    {
        char *ret = dest;
        assert(dest != NULL && src != NULL);
        while (*dest++ = *src++) {}
        return ret;
    }
}

int main()
{
    char *a = (char*)malloc(10);
    a = nullptr; // test
    char *word = "word";
    czxyl::my_strcpy(a, word);
    std::cout << a;
}

这里触发了断言. 断点进去发现nullptr已经变成为了NULL:

dest : 0x00000000 <NULL>

我的猜测就是, nullptr的类型是nullptr_t, 而这里又没有相应参数的多态函数. 所以实参变成pointer类型.

这里我的问题:

  1. 哪些情况下会发生这样的cast
  2. 这样的隐式转换会不会有坑, 比如你本意想传入个nullptr, 但是参数又是一个char*之类的推导类型. 偏偏函数又需要处理nullptr这个情况, 并且是NULL应付不了的, 比如里面会遇到有foo(int x)foo(std::nullptr_t nullp)两个重载函数.

2个回答

︿ 3

1.哪些情况下会发生这样的cast

关键字nullptr是一种指针字面量,表达式nullptr是一个空指针常量,这样的常量可以被隐式转换到任意指针类型。当一指针类型的对象被nullptr_t类型的表达式(比方说nullptr)初始化/赋值时,会发生这样的转换。

注:这里没有发生你所说的到NULL类型的转换,也不存在NULL类型。这里发生的是从nullptr_tchar *的转换。NULL实际上是一个宏,在C++11下已经不再需要使用这个宏了。

2.14.7.1 The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t.

3.9.10 A value of type std::nullptr_t is a null pointer constant (4.10).[...]

4.10.1 A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.[...]

2.会不会有坑?

我不知道你说的坑具体指什么,但可以肯定的是:

  • intcv T *构成重载时,nullptr作为参数会调用后者。

  • cv T *std::nullptr_t构成重载时,会调用后者。

  • intbool构成重载时,会调用后者。

  • boolstd::nullptr_t构成重载时,会调用后者。

  • cv T *bool构成重载时,是模糊调用。

即优先级: int(不能调用) < bool = cv T * < std::nullptr_t。

在这里分析其中的两种情况:

你问题中举例的情况,foo(nullptr)会调用后者,即foo(std::nullptr_t)(你给出的声明有拼写错误)。因为表达式nullptr的类型是std::nullptr_t,并且nullptr不能被隐式转换到int类型。(如果没有foo(std::nullptr_t),那么调用会失败)

nullptr不能被隐式转换到int类型的原因是:nullptr的类型是std::nullptr_t,std::nullptr_t类型可以被隐式转换到bool,bool类型可以被隐式转换到int,且不存在直接从std::nullptr_t到int的转换。所以从std::nullptr_t到int需要一次boolean conversions和一次integral promotions,但在一个标准转换序列(standard conversion sequence)中,这两种转换至多只能有一种出现一次。(从std::nullptr_t到cv T *再到int也不可行)

4.12.1 [...]A prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

4.1 Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:

  • Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.

  • Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.

  • Zero or one qualification conversion.

如果foo(cv T *)foo(std::nullptr_t)构成重载,根据重载决议foo(nullptr)将会调用后者。因为调用前者需要一次pointer conversion,而调用后者不需要转换。详细分析从略,需要可以在评论区追问。

︿ 2

C++11 引入了新的关键字来代表空指针常数:nullptr,将空指针和整数 0 的概念拆开。 nullptr 的类型为nullptr_t,能隐式转换为任何指针或是成员指针的类型,也能和它们进行相等或不等的比较。 而nullptr不能隐式转换为整数,也不能和整数做比较。
为了向下兼容,0 仍可代表空指针常数。
char* pc = nullptr; // OK
int * pi = nullptr; // OK
int i = nullptr; // error

所以assert(dest != NULL && src != NULL); 这句里的比较就出错了。

很久不用C++了,不知道说的对不对仅供参考。