Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conversion from integral constant expression to null-pointer

Consider following code:

#include <memory>

void f( std::shared_ptr<int> ) {}

int main()
{
    f( 0 );               // compiles fine in gcc and clang
    f( 1 - 1 );           // compiles fine in gcc, fails in clang
    constexpr int i = 0;
    f( i );               // fails to compile in gcc and clang
    f( i - 0 );           // compiles fine in gcc, fails in clang
}

why only f( i ) fails to compile, though i should be evaluated as compile time constant with value 0?

PS checked with g++ v 5.1.0, it accepts all variants except f(i); in both c++11 and c++14 mode PPS checked with clang 3.7, it rejects all variants except literal 0 in both c++11 and c++14 mode

like image 302
Slava Avatar asked Dec 29 '15 17:12

Slava


People also ask

Is null and nullptr the same?

nullptr is a keyword that can be used at all places where NULL is expected. Like NULL, nullptr is implicitly convertible and comparable to any pointer type. Unlike NULL, it is not implicitly convertible or comparable to integral types.

What does nullptr do in C++?

The nullptr keyword represents a null pointer value. Use a null pointer value to indicate that an object handle, interior pointer, or native pointer type does not point to an object. Use nullptr with either managed or native code.

How do you make a pointer null in C++?

We can use nullptr to explicitly initialize or assign a pointer a null value. In the above example, we use assignment to set the value of ptr2 to nullptr , making ptr2 a null pointer. Use nullptr when you need a null pointer literal for initialization, assignment, or passing a null pointer to a function.

What is integral constant expression?

An integral constant expression is an expression that can be evaluated at compile time, and whose type is integral or an enumeration. The situations that require integral constant expressions include array bounds, enumerator values, case labels, bit-field sizes, static member initializers, and value template arguments.


2 Answers

This is a gcc bug. Defect report 903: Value-dependent integral null pointer constants which is a defect report against C++11(it has CD3 status), makes it so that only an integer literal 0 is considered a null pointer constant.

It changed section 4.10 [conv.ptr] paragraph 1 amongst other changes from:

A null pointer constant is an integral constant expression (5.19 [expr.const]) prvalue of integer type that evaluates to zero [...]

to:

A null pointer constant is an integer literal (2.14.2 [lex.icon]) with value zero [...]

This is listed as an incompatibility against C++03, from section C.2.2 Clause 4: standard conversions [diff.cpp03.conv] which says:

Change: Only literals are integer null pointer constants
Rationale: Removing surprising interactions with templates and constant expressions
Effect on original feature: Valid C++ 2003 code may fail to compile or produce different results in this International Standard, as the following example illustrates:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
  f(0*N); // calls #2; used to call #1
}

The following gcc bug report [C++11] [DR 903] zero-valued integer constant expression should prefer conversion to pointer shows that the gcc team originally thought this was a C++17 change but later changed it to be in effect in C++11.

We can see in the head revision of gcc(6.0) this is fixed (see it live) and produces a diagnostic for all the cases clang does:

error: could not convert '(1 - 1)' from 'int' to 'std::shared_ptr<int>'
 f( 1 - 1 );           // compiles fine in gcc, fails in clang
    ~~^~~

error: could not convert 'i' from 'const int' to 'std::shared_ptr<int>'
 f( i );               // fails to compile in gcc and clang
      ^

error: could not convert '(0 - 0)' from 'int' to 'std::shared_ptr<int>'
 f( i - 0 );           // compiles fine in gcc, fails in clang
    ~~^~~
like image 65
Shafik Yaghmour Avatar answered Sep 19 '22 12:09

Shafik Yaghmour


Because a null pointer constant is defined not just as a compile-time integral constant with value 0, but as an integer literal with value zero (or as a prvalue of type std::nullptr_t, of course). C++14 (N4140), 4.10/1.

So actually, only the first line f(0) should compile, all the other ones should provoke at least a diagnostic message from a conforming compiler.

like image 21
Angew is no longer proud of SO Avatar answered Sep 19 '22 12:09

Angew is no longer proud of SO