This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 113d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-03-20


903. Value-dependent integral null pointer constants

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD3     Submitter: Doug Gregor     Date: 22 May, 2009

[Moved to DR status at the April, 2013 meeting.]

Consider the following example:

    void f(int*);
    void f(...);

    template <int N> void g() {
      f(N);
    }

    int main() {
      g<0>();
      g<1>();
    }

The call to f in g is not type-dependent, so the overload resolution must be done at definition time rather than at instantiation time. As a result, both of the calls to g will result in calls to f(...), i.e., N will not be a null pointer constant, even if the value of N is 0.

It would be most consistent to adopt a rule that a value-dependent expression can never be a null pointer constant, even in cases like

    template <int N> void g() {
      int* p = N;
    }

This would always be ill-formed, even when N is 0.

John Spicer: It's clear that this treatment is required for overload resolution, but it seems too expansive given that there are other cases in which the value of a template parameter can affect the validity of the program, and an implementation is forbidden to issue a diagnostic on a template definition unless there are no possible valid specializations.

Notes from the July, 2009 meeting:

There was a strong consensus among the CWG that only the literal 0 should be considered a null pointer constant, not any arbitrary zero-valued constant expression as is currently specified.

Proposed resolution (October, 2012):

  1. Change 7.3.12 [conv.ptr] paragraph 1 as follows:

  2. A null pointer constant is an integral constant expression (7.7 [expr.const]) prvalue of integer type that evaluates to integer literal (5.13.2 [lex.icon]) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted...
  3. Change 7.7 [expr.const] paragraph 3 as follows:

  4. ...[Note: Such expressions may be used as array bounds (9.3.4.5 [dcl.array], 7.6.2.8 [expr.new]), as bit-field lengths (11.4.10 [class.bit]), as enumerator initializers if the underlying type is not fixed (9.7.1 [dcl.enum]), as null pointer constants (7.3.12 [conv.ptr]), and as alignments (9.12.2 [dcl.align]). —end note]...
  5. Change 9.4 [dcl.init] paragraph 5 as follows:

  6. To zero-initialize an object or reference of type T means:

  7. Change 13.4.3 [temp.arg.nontype] paragraph 5 as follows:

  8. Change 14.4 [except.handle] paragraph 3 as follows:

  9. ...[Note: A throw-expression whose operand is an integral constant expression of integer type that evaluates to integer literal with value zero does not match a handler of pointer or pointer to member type. —end note]. [Example: ...
  10. Add a new section to C.6 [diff.cpp03] as follows:

  11. C.2.x Clause 4: standard conversions [diff.cpp03.conv]

    7.3.12 [conv.ptr]
    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
      }
    

Additional note (January, 2013):

Concerns were raised at the Portland (October, 2012) meeting that the value false has been used in existing code as a null pointer constant, and such code would be broken by this change. This issue has been returned to "review" status to allow discussion of whether to accommodate such code or not.