Document number:  J16/06-0196 = WG21 N2126
Date:  2006-11-05
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2003
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


C++ Standard Core Language Defect Reports, Revision 44


This document contains the C++ core language issues that have been categorized as Defect Reports by the Committee (J16 + WG21), that is, issues with status "DR," "WP," and "TC1," along with their proposed resolutions. ONLY RESOLUTIONS FOR ISSUES WITH TC1 STATUS ARE PART OF THE INTERNATIONAL STANDARD FOR C++. The other issues are provided for informational purposes only, as an indication of the intent of the Committee. They should not be considered definitive until or unless they appear in an approved Technical Corrigendum or revised International Standard for C++.

This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:

For more information, including a description of the meaning of the issue status codes and instructions on reporting new issues, please see the Active Issues List.


Issues with "DR" Status


505. Conditionally-supported behavior for unknown character escapes

Section: 2.13.2  lex.ccon     Status: DR     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 2.13.2  lex.ccon paragraph 3 states,

If the character following a backslash is not one of those specified, the behavior is undefined.

Paper J16/04-0167=WG21 N1727 suggests that such character escapes be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.

Proposed resolution (April, 2006):

Change the next-to-last sentence of 2.13.2  lex.ccon paragraph 3 from:

If the character following a backslash is not one of those specified, the behavior is undefined.

to:

Escape sequences in which the character following the backslash is not listed in Table 6 are conditionally-supported, with implementation-defined semantics.



514. Is the initializer for a namespace member in the scope of the namespace?

Section: 3.4.1  basic.lookup.unqual     Status: DR     Submitter: Mike Miller     Date: 24 Mar 2005

[Voted into WP at the October, 2006 meeting.]

Is the following code well-formed?

    namespace N {
      int i;
      extern int j;
    }
    int N::j = i;

The question here is whether the lookup for i in the initializer of N::j finds the declaration in namespace N or not. Implementations differ on this question.

If N::j were a static data member of a class, the answer would be clear: both 3.4.1  basic.lookup.unqual paragraph 12 and 8.5  dcl.init paragraph 11 say that the initializer “is in the scope of the member's class.” There is no such provision for namespace members defined outside the namespace, however.

The reasoning given in 3.4.1  basic.lookup.unqual may be instructive:

A name used in the definition of a static data member of class X (9.4.2  class.static.data) (after the qualified-id of the static member) is looked up as if the name was used in a member function of X.

It is certainly the case that a name used in a function that is a member of a namespace is looked up in that namespace (3.4.1  basic.lookup.unqual paragraph 6), regardless of whether the definition is inside or outside that namespace. Initializers for namespace members should probably be looked up the same way.

Proposed resolution (April, 2006):

Add a new paragraph following 3.4.1  basic.lookup.unqual paragraph 12:

If a variable member of a namespace is defined outside of the scope of its namespace then any name used in the definition of the variable member (after the declarator-id) is looked up as if the definition of the variable member occurred in its namespace. [Example:

    namespace N {
      int i = 4;
      extern int j;
    }

    int i = 2;

    int N::j = i;	// N::j == 4

end example]




557. Does argument-dependent lookup cause template instantiation?

Section: 3.4.2  basic.lookup.argdep     Status: DR     Submitter: Mike Miller     Date: 8 February 2006

[Voted into WP at the October, 2006 meeting.]

One might assume from 14.7.1  temp.inst paragraph 1 that argument-dependent lookup would require instantiation of any class template specializations used in argument types:

Unless a class template specialization has been explicitly instantiated (14.7.2  temp.explicit) or explicitly specialized (14.7.3  temp.expl.spec), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.

A complete class type is required to determine the associated classes and namespaces for the argument type (to determine the class's bases) and to determine the friend functions declared by the class, so the completeness of the class type certainly “affects the semantics of the program.”

This conclusion is reinforced by the second bullet of 3.4.2  basic.lookup.argdep paragraph 2:

A class template specialization is a class type, so the second bullet would appear to apply, requiring the specialization to be instantiated in order to determine its base classes.

However, bullet 8 of that paragraph deals explicitly with class template specializations:

Note that the class template specialization itself is not listed as an associated class, unlike other class types, and there is no mention of base classes. If bullet 8 were intended as a supplement to the treatment of class types in bullet 2, one would expect phrasing along the lines of, “In addition to the associated namespaces and classes for all class types...” or some such; instead, bullet 8 reads like a self-contained and complete specification.

If argument-dependent lookup does not cause implicit instantiation, however, examples like the following fail:

    template <typename T> class C {
        friend void f(C<T>*) { }
    };
    void g(C<int>* p) {
        f(p);    // found by ADL??
    }

Implementations differ in whether this example works or not.

Proposed resolution (April, 2006):

  1. Change bullet 2 of 3.4.2  basic.lookup.argdep paragraph 2 as indicated:

  2. Delete bullet 8 of 3.4.2  basic.lookup.argdep paragraph 2:




305. Name lookup in destructor call

Section: 3.4.5  basic.lookup.classref     Status: DR     Submitter: Mark Mitchell     Date: 19 May 2001

[Voted into WP at the October, 2006 meeting.]

I believe this program is invalid:

    struct A {
    };

    struct C {
      struct A {};
      void f ();
    };

    void C::f () {
      ::A *a;
      a->~A ();
    }
The problem is that 3.4.5  basic.lookup.classref says that you have to look up A in both the context of the pointed-to-type (i.e., ::A), and in the context of the postfix-expression (i.e., the body of C::f), and that if the name is found in both places it must name the same type in both places.

The EDG front end does not issue an error about this program, though.

Am I reading the standardese incorrectly?

John Spicer: I think you are reading it correctly. I think I've been hoping that this would get changed. Unlike other dual lookup contexts, this is one in which the compiler already knows the right answer (the type must match that of the left hand of the -> operator). So I think that if either of the types found matches the one required, it should be sufficient. You can't say a->~::A(), which means you are forced to say a->::A::~A(), which disables the virtual mechanism. So you would have to do something like create a local typedef for the desired type.

See also issues 244, 399, and 466.

Proposed resolution (April, 2006):

  1. Remove the indicated text from 3.4.5  basic.lookup.classref paragraph 2:

    If the id-expression in a class member access (5.2.5  expr.ref) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C...
  2. Change 3.4.5  basic.lookup.classref paragraph 3 as indicated:

    If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. and If the type T of the object expression is of a class type C (or of pointer to a class type C), the type-name is also looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression. At least one of the lookups shall find a name that refers to (possibly cv-qualified) T. [Example:
        struct A { };
    
        struct B {
          struct A { };
          void f(::A* a);
        };
    
        void B::f(::A* a) {
          a->~A();  // OK, lookup in *a finds the injected-class-name
        }
    
    end example]

[Note: this change also resolves issue 414.]




414. Multiple types found on destructor lookup

Section: 3.4.5  basic.lookup.classref     Status: DR     Submitter: John Spicer     Date: 1 May 2003

[Voted into WP at the October, 2006 meeting.]

By 3.4.5  basic.lookup.classref paragraph 3, the following is ill-formed because the two lookups of the destructor name (in the scope of the class of the object and in the surrounding context) find different Xs:

  struct X {};
  int main() {
    X x;
    struct X {};
    x.~X();  // Error?
  }

This is silly, because the compiler knows what the type has to be, and one of the things found matches that. The lookup should require only that one of the lookups finds the required class type.

Proposed resolution (April, 2005):

This issue is resolved by the resolution of issue 305.




521. Requirements for exceptions thrown by allocation functions

Section: 3.7.3.1  basic.stc.dynamic.allocation     Status: DR     Submitter: Alisdair Meredith     Date: 22 May 2005

[Voted into WP at the October, 2006 meeting.]

According to 3.7.3.1  basic.stc.dynamic.allocation paragraph 3,

Any other allocation function that fails to allocate storage shall only indicate failure by throwing an exception of class std::bad_alloc (18.4.2.1  lib.bad.alloc) or a class derived from std::bad_alloc.

Shouldn't this statement have the usual requirements for an unambiguous and accessible base class?

Proposed resolution (April, 2006):

Change the last sentence of 3.7.3.1  basic.stc.dynamic.allocation paragraph 3 as indicated:

Any other allocation function that fails to allocate storage shall only indicate failure only by throwing an exception of class std::bad_alloc (18.4.2.1  lib.bad.alloc) or a class derived from std::bad_alloc a type that would match a handler (15.3  except.handle) of type std::bad_alloc (18.4.2.1  lib.bad.alloc).



480. Is a base of a virtual base also virtual?

Section: 4.11  conv.mem     Status: DR     Submitter: Mark Mitchell     Date: 18 Oct 2004

[Voted into WP at the October, 2006 meeting.]

When the Standard refers to a virtual base class, it should be understood to include base classes of virtual bases. However, the Standard doesn't actually say this anywhere, so when 4.11  conv.mem (for example) forbids casting to a derived class member pointer from a virtual base class member pointer, it could be read as meaning:

  struct B {};
  struct D : public B {};
  struct D2 : virtual public D {};

  int B::*p;
  int D::*q;

  void f() {
    static_cast<int D2::*>(p);  // permitted
    static_cast<int D2::*>(q);  // forbidden
  }

Proposed resolution (October, 2005):

  1. Change 4.11  conv.mem paragraph 2 as indicated:

  2. ...If B is an inaccessible (clause 11  class.access), ambiguous (10.2  class.member.lookup) or virtual (10.1  class.mi) base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed...
  3. Change 5.2.9  expr.static.cast paragraph 2 as indicated:

  4. ...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...
  5. Change 5.2.9  expr.static.cast paragraph 9 as indicated:

  6. ...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...



506. Conditionally-supported behavior for non-POD objects passed to ellipsis

Section: 5.2.2  expr.call     Status: DR     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 5.2.2  expr.call paragraph 7 states:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7  lib.support.runtime). The lvalue-to-rvalue (4.1  conv.lval), array-to-pointer (4.2  conv.array), and function-to-pointer (4.3  conv.func) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9  class), the behavior is undefined.

Paper J16/04-0167=WG21 N1727 suggests that passing a non-POD object to ellipsis be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.

Proposed resolution (October, 2005):

Change 5.2.2  expr.call paragraph 7 as indicated:

...After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. Passing an argument of non-POD class type (clause 9) with no corresponding parameter is conditionally-supported, with implementation-defined semantics.



367. throw operator allowed in constant expression?

Section: 5.19  expr.const     Status: DR     Submitter: Martin v. Loewis     Date: 29 July 2002

[Voted into WP at the October, 2006 meeting.]

The following translation unit appears to be well-formed.

int x[true?throw 4:5];

According to 5.19  expr.const, this appears to be an integral constant expression: it is a conditional expression, involves only literals, and no assignment, increment, decrement, function-call, or comma operators. However, if this is well-formed, the standard gives no meaning to this declaration, since the array bound (8.3.4  dcl.array paragraph 1) cannot be computed.

I believe the defect is that throw expressions should also be banned from constant expressions.

Notes from October 2002 meeting:

We should also check on new and delete.

Notes from the April, 2005 meeting:

Although it could be argued that all three of these operators potentially involve function calls — throw to std::terminate, new and delete to the corresponding allocation and deallocation functions — and thus would already be excluded from constant expressions, this reasoning was considered to be too subtle to allow closing the issue with no change. A modification that explicitly clarifies the status of these operators will be drafted.

Proposed resolution (October, 2005):

Change the last sentence of 5.19  expr.const as indicated:

In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call function call (including new-expressions and delete-expressions), or comma operators, or throw-expressions shall not be used.

Note: this sentence is also changed by the resolution of issue 530.




477. Can virtual appear in a friend declaration?

Section: 7.1.2  dcl.fct.spec     Status: DR     Submitter: Daveed Vandevoorde     Date: 23 Sep 2004

[Voted into WP at the October, 2006 meeting.]

I couldn't find wording that makes it invalid to say friend virtual... The closest seems to be 7.1.2  dcl.fct.spec paragraph 5, which says:

The virtual specifier shall only be used in declarations of nonstatic class member functions that appear within a member-specification of a class definition; see 10.3  class.virtual.

I don't think that excludes a friend declaration (which is a valid member-specification by 9.2  class.mem).

John Spicer: I agree that virtual should not be allowed on friend declarations. I think the wording in 7.1.2  dcl.fct.spec is intended to be the declaration of a function within its class, although I think the wording should be improved to make it clearer.

Proposed resolution (October, 2005):

Change 7.1.2  dcl.fct.spec paragraphs 5-6 as indicated:

The virtual specifier shall only be used only in declarations the initial declaration of a non-static class member functions that appear within a member-specification of a class definition function; see 10.3  class.virtual.

The explicit specifier shall be used only in declarations the declaration of constructors a constructor within a its class definition; see 12.3.1  class.conv.ctor.




516. Use of signed in bit-field declarations

Section: 7.1.5.2  dcl.type.simple     Status: DR     Submitter: comp.std.c++     Date: 25 Apr 2005

[Voted into WP at the October, 2006 meeting.]

7.1.5.2  dcl.type.simple paragraph 3 reads,

It is implementation-defined whether bit-fields and objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects and bit-fields to be signed; it is redundant with other integral types.

The last sentence in that quote is misleading w.r.t. bit-fields. The first sentence in that quote is correct but incomplete.

Proposed fix: change the two sentences to read:

It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects signed; it is redundant with other integral types except when declaring bit-fields (9.6  class.bit).

Proposed resolution (October, 2005):

Change 7.1.5.2  dcl.type.simple paragraph 3 as indicated:

When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [Note: It is implementation-defined whether bit-fields and objects of char type and certain bit-fields (9.6  class.bit) are represented as signed or unsigned quantities. The signed specifier forces bit-fields and char objects and bit-fields to be signed; it is redundant with other integral types in other contexts. end note]



540. Propagation of cv-qualifiers in reference-to-reference collapse

Section: 7.3.1  namespace.def     Status: DR     Submitter: Russell Yanofsky     Date: 24 September 2005

[Voted into the WP at the October, 2006 meeting as part of paper J16/06-0188 = WG21 N2118.]

The resolution of issue 106 specifies that an attempt to create a type “reference to cv1 T,” where T is a typedef or template parameter of the type “reference to cv2 S,” actually creates the type “reference to cv12 S,” where cv12 is the union of the two sets of cv-qualifiers.

One objection that has been raised to this resolution is that it is inconsistent with the treatment of cv-qualification and references specified in 8.3.2  dcl.ref paragraph 1, which says that cv-qualifiers applied to a typedef or template argument that is a reference type are ignored. For example:

    typedef int& intref;
    const intref r1;       // reference to int
    const intref& r2;      // reference to const int

In fact, however, these two declarations are quite different. In the declaration of r1, const applies to a “top-level” reference, while in the declaration of t2, it occurs under a reference. In general, cv-qualifiers that appear under a reference are preserved, even if the type appears in a context in which top-level cv-qualification is removed, for example, in determining the type of a function from parameter types (8.3.5  dcl.fct paragraph 3) and in template argument deduction (14.8.2.1  temp.deduct.call paragraph 2).

Another objection to the resolution is that type composition gives different results in a single declaration than it does when separated into two declarations. For example:

    template <class T>
    struct X {
       typedef T const T_const;
       typedef T_const& type1;
       typedef T const& type2;
    };

    X<int&>::type1 t1;    // int&
    X<int&>::type2 t2;    // int const&

The initial motivation for the propagation of cv-qualification during reference-to-reference collapse was to prevent inadvertent loss of cv-qualifiers in contexts in which it could make a difference. For example, if the resolution were changed to discard, rather than propagate, embedded cv-qualification, overload resolution could surprisingly select a non-const version of a member function:

   struct X {
       void g();
       void g() const;
   };

   template <typename T> struct S {
       static void f(const T& t) {
           t.g();    // const or non-const???
       }
   };

   X x;

   void q() {
       S<X>::f(x);    // calls X::g() const
       S<X&>::f(x);   // calls X::g()
   }

Another potentially-surprising outcome of dropping embedded cv-qualifiers would be:

   template <typename T> struct A {
       void f(T&);          // mutating version
       void f(const T&);    // non-mutating version
   };

   A<int&> ai;    // Ill-formed: A<int&> declares f(int&) twice

On the other hand, those who would like to see the resolution changed to discard embedded cv-qualifiers observe that these examples are too simple to be representative of real-world code. In general, it is unrealistic to expect that a template written with non-reference type parameters in mind will automatically work correctly with reference type parameters as a result of applying the issue 106 resolution. Instead, template metaprogramming allows the template author to choose explicitly whether cv-qualifiers are propagated or dropped, according to the intended use of the template, and it is more important to respect the reasonable intuition that a declaration involving a template parameter will not change the type that the parameter represents.

As a sample of real-world code, tr1::tuple was examined. In both cases — the current resolution of issue 106 and one in which embedded cv-qualifiers were dropped — some metaprogramming was required to implement the intended interface, although the version reflecting the revised resolution was somewhat simpler.

Notes from the October, 2005 meeting:

The consensus of the CWG was that the resolution of issue 106 should be revised not to propagate embedded cv-qualification.

Note (February, 2006):

The wording included in the rvalue-reference paper, J16/06-0022 = WG21 N1952, incorporates changes intended to implement the October, 2005 consensus of the CWG.




454. When is a definition of a static data member required?

Section: 9.4.2  class.static.data     Status: DR     Submitter: Gennaro Prota     Date: 18 Jan 2004

[Voted into WP at the October, 2006 meeting.]

As a result of the resolution of core issue 48, the current C++ standard is not in sync with existing practice and with user expectations as far as definitions of static data members having const integral or const enumeration type are concerned. Basically what current implementations do is to require a definition only if the address of the constant is taken. Example:

void f() {

  std::string s;
  ... 

  // current implementations don't require a definition
  if (s.find('a', 3) == std::string::npos) {
   ...
  }

To the letter of the standard, though, the above requires a definition of npos, since the expression std::string::npos is potentially evaluated. I think this problem would be easily solved with simple changes to 9.4.2  class.static.data paragraph 4, 9.4.2  class.static.data paragraph 5 and 3.2  basic.def.odr paragraph 3.

Suggested resolution:

Replace 9.4.2  class.static.data paragraph 4 with:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be [note1] an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. No definition of the member is required, unless an lvalue expression that designates it is potentially evaluated and either used as operand to the built-in unary & operator [note 2] or directly bound to a reference.

If a definition exists, it shall be at namespace scope and shall not contain an initializer.

In 9.4.2  class.static.data paragraph 5 change

There shall be exactly one definition of a static data member that is used in a program; no diagnostic is required; see 3.2.

to

Except as allowed by 9.4.2 par. 4, there shall be exactly one definition of a static data member that is potentially evaluated (3.2) in a program; no diagnostic is required.

In 3.2  basic.def.odr paragraph 3 add, at the beginning:

Except for the omission allowed by 9.4.2, par. 4, ...

[note 1] Actually it shall be a "= followed by a constant-expression". This could probably be an editorial fix, rather than a separate DR.

[note 2] Note that this is the case when reinterpret_cast-ing to a reference, like in

struct X { static const int value = 0; };
const char & c = reinterpret_cast<const char&>(X::value);
See 5.2.10  expr.reinterpret.cast/10

More information, in response to a question about why issue 48 does not resolve the problem:

The problem is that the issue was settled in a way that solves much less than it was supposed to solve; that's why I decided to file, so to speak, a DR on a DR.

I understand this may seem a little 'audacious' on my part, but please keep reading. Quoting from the text of DR 48 (emphasis mine):

Originally, all static data members still had to be defined outside the class whether they were used or not.

But that restriction was supposed to be lifted [...]

In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.

The corresponding resolution doesn't reflect this intent, with the definition being still required in most situations anyway: it's enough that the constant appears outside a place where constants are required (ignoring the obvious cases of sizeof and typeid) and you have to provide a definition. For instance:

  struct X {
   static const int c = 1;
  };

  void f(int n)
  {
   if (n == X::c)   // <-- potentially evaluated
    ...
  }

<start digression>

Most usages of non-enum BOOST_STATIC_COSTANTs, for instance, are (or were, last time I checked) non-conforming. If you recall, Paul Mensonides pointed out that the following template

// map_integral

template<class T, T V> struct map_integral : identity<T> {
  static const T value = V;
};

template<class T, T V> const T map_integral<T, V>::value;

whose main goal is to map the same couples (type, value) to the same storage, also solves the definition problem. In this usage it is an excellent hack (if your compiler is good enough), but IMHO still a hack on a language defect.

<end digression>

What I propose is to solve the issue according to the original intent, which is also what users expect and all compilers that I know of already do. Or, in practice, we would have a rule that exists only as words in a standard document.

PS: I've sent a copy of this to Mr. Adamczyk to clarify an important doubt that occurred to me while writing this reply:

if no definition is provided for an integral static const data member is that member an object? Paragraph 1.8/1 seems to say no, and in fact it's difficult to think it is an object without assuming/pretending that a region of storage exists for it (an object *is* a region of storage according to the standard).

I would think that when no definition is required we have to assume that it could be a non-object. In that case there's nothing in 3.2 which says what 'used' means for such an entity and the current wording would thus be defective. Also, since the name of the member is an lvalue and 3.10/2 says an lvalue refers to an object we would have another problem.

OTOH the standard could pretend it is always an object (though the compiler can optimize it away) and in this case it should probably make a special case for it in 3.2/2.

Notes from the March 2004 meeting:

We sort of like this proposal, but we don't feel it has very high priority. We're not going to spend time discussing it, but if we get drafting for wording we'll review it.

Proposed resolution (October, 2005):

  1. Change the first two sentences of 3.2  basic.def.odr paragraph 2 from:

    An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19  expr.const), is the operand of the sizeof operator (5.3.3  expr.sizeof), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8  expr.typeid). An object or non-overloaded function is used if its name appears in a potentially-evaluated expression.

    to

    An expression that is the operand of the sizeof operator (5.3.3  expr.sizeof) is unevaluated, as is an expression that is the operand of the typeid operator if it is not an lvalue of a polymorphic class type (5.2.8  expr.typeid); all other expressions are potentially evaluated. An object or non-overloaded function whose name appears as a potentially-evaluated expression is used, unless it is an object that satisfies the requirements for appearing in an integral constant expression (5.19  expr.const) and the lvalue-to-rvalue conversion (4.1  conv.lval) is immediately applied.
  2. Change the first sentence of 9.4.2  class.static.data paragraph 2 as indicated:

  3. If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which whose constant-expression shall be an integral constant expression (5.19  expr.const).



58. Signedness of bit fields of enum type

Section: 9.6  class.bit     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Voted into WP at the October, 2006 meeting.]

Section 9.6  class.bit paragraph 4 needs to be more specific about the signedness of bit fields of enum type. How much leeway does an implementation have in choosing the signedness of a bit field? In particular, does the phrase "large enough to hold all the values of the enumeration" mean "the implementation decides on the signedness, and then we see whether all the values will fit in the bit field", or does it require the implementation to make the bit field signed or unsigned if that's what it takes to make it "large enough"?

(See also issue 172.)

Note (March, 2005): Clark Nelson observed that there is variation among implementations on this point.

Notes from April, 2005 meeting:

Although implementations enjoy a great deal of latitude in handling bit-fields, it was deemed more user-friendly to ensure that the example in paragraph 4 will work by requiring implementations to use an unsigned underlying type if the enumeration type has no negative values. (If the implementation is allowed to choose a signed representation for such bit-fields, the comparison against TRUE will be false.)

In addition, it was observed that there is an apparent circularity between 7.2  dcl.enum paragraph 7 and 9.6  class.bit paragraph 4 that should be resolved.

Proposed resolution (April, 2006):

  1. Replace 7.2  dcl.enum paragraph 7, deleting the embedded footnote 85, with the following:

    For an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|-K,|emax|) and equal to 2M-1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise. It is possible to define an enumeration that has values not defined by any of its enumerators.
  2. Add the indicated text to the second sentence of 9.6  class.bit paragraph 4:

    If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type (7.2  dcl.enum), the original enumerator value and the value of the bit-field shall compare equal.



484. Can a base-specifier name a cv-qualified class type?

Section: 10  class.derived     Status: DR     Submitter: Richard Corden     Date: 21 Oct 2004

[Voted into WP at the October, 2006 meeting.]

Issue 298, recently approved, affirms that cv-qualified class types can be used as nested-name-specifiers. Should the same be true for base-specifiers?

Rationale (April, 2005):

The resolution of issue 298 added new text to 9.1  class.name paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.

Additional notes (June, 2005):

It's not completely clear what it means to have a cv-qualified type as a base-specifier. The original proposed resolution for issue 298 said that “the cv-qualifiers are ignored,” but that wording is not in the resolution that was ultimately approved.

If the cv-qualifiers are not ignored, does that mean that the base-class subobject should be treated as always similarly cv-qualified, regardless of the cv-qualification of the derived-class lvalue used to access the base-class subobject? For instance:

    typedef struct B {
        void f();
        void f() const;
        int i;
    } const CB;

    struct D: CB { };

    void g(D* dp) {
        dp->f();    // which B::f?
        dp->i = 3;  // permitted?
    }

Proposed resolution (October, 2005):

  1. Change 9.1  class.name paragraph 5 as indicated:

  2. A typedef-name (7.1.3  dcl.typedef) that names a class type, or a cv-qualified version thereof, is also a class-name, but class-name. If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored. A typedef-name shall not be used as the identifier in a class-head.
  3. Delete 7.1.3  dcl.typedef paragraph 8:

  4. [Note: if the typedef-name is used where a class-name (or enum-name) is required, the program is ill-formed. For example,

        typedef struct {
            S();     // error: requires a return type because S is
                      // an ordinary member function, not a constructor
        } S;
    

    end note]




494. Problems with the resolution of issue 45

Section: 11  class.access     Status: DR     Submitter: Lloyd J. Lewins     Date: 17 Dec 2004

[Voted into WP at the October, 2006 meeting.]

The proposed resolution for issue 45 inserts the following sentence after 11  class.access paragraph 1:

A member of a class can also access all names as the class of which it is a member.

I don't think that this is correctly constructed English. I see two possibilities:

  1. This is a typo, and the correct change is:

    A member of a class can also access all names of the class of which it is a member.

  2. The intent is something more like:

    A member of a nested class can also access all names accessible by any other member of the class of which it is a member.

[Note: this was editorially corrected at the time defect resolutions were being incorporated into the Working Paper to read, “...can also access all the names declared in the class of which it is a member,” which is essentially the same as the preceding option 1.]

I would prefer to use the language proposed for 11.8  class.access.nest:

A nested class is a member and as such has the same access rights as any other member.

A second problem is with the text in 11.4  class.friend paragraph 2:

[Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,
    class A {
        class B { };
        friend class X;
    };
    class X : A::B {     // ill-formed: A::B cannot be accessed
                         // in the base-clause for X
        A::B mx;         // OK: A::B used to declare member of X
        class Y: A::B {  // OK: A::B used to declare member of X
            A::B my;     // ill-formed: A::B cannot be accessed
                         // to declare members of nested class of X
        };
    };
end note]

This seems to be an oversight. The proposed change to 11.8  class.access.nest paragraph 1 would appear to have eliminated the restrictions on nested class access. However, at least one compiler (gcc 3.4.3) doesn't appear to take my view, and continues with the restrictions on access by classes within a friend class, while implementing the rest of the resolution of issue 45.

Note (March, 2005):

Andreas Hommel: I think issue 45 requires an additional change in 9.7  class.nest paragraph 4:

Like a member function, a friend function (11.4  class.friend) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4  class.static) and has no special access rights to members of an enclosing class.

I believe the “no special access rights” language should be removed.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




500. Access in base-specifiers of friend and nested classes

Section: 11.4  class.friend     Status: DR     Submitter: Andreas Hommel     Date: 25 Jan 2005

[Voted into WP at the October, 2006 meeting.]

I don't know the reason for this distinction, but it seems to be surprising that Base::A is legal and D is illegal in this example:

    class D;
    class Base 
    { 
        class A;
        class B;
        friend class D;
    }; 
    class Base::B 
    {
    };
    class Base::A : public Base::B  // OK because of issue 45
    { 
    };
    class D : public Base::B        // illegal because of 11.4p4
    { 
    };

Shouldn't this be consistent (either way)?

Notes from the April, 2005 meeting:

In discussing issue 372, the CWG decided that access in the base-specifiers of a class should be the same as for its members, and that resolution will apply to friend declarations, as well.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




534. template-names and operator-function-ids

Section: 14  temp     Status: DR     Submitter: Jens Maurer     Date: 5 October 2005

[Voted into WP at the October, 2006 meeting.]

Taken literally, 14  temp paragraph 2 does not permit operator functions to be templates:

In a function template declaration, the declarator-id shall be a template-name (i.e., not a template-id).

and, in 14.2  temp.names paragraph 1, a template-name is defined to be simply an identifier.

Issue 301 considered and rejected the idea of changing the definition of template-name to include operator-function-ids and conversion-function-ids. Either that decision should be reconsidered or the various references in the text to template-name should be examined to determine if they should also mention the non-identifier possibilities for function template names.

Proposed resolution (April, 2006):

This issue is resolved by the resolution of issue 301.




301. Syntax for template-name

Section: 14.2  temp.names     Status: DR     Submitter: Mark Mitchell     Date: 24 Jul 2001

[Voted into WP at the October, 2006 meeting.]

The grammar for a template-name is:

That's not right; consider:

    template <class T> T operator+(const T&, const T&);
    template <> S operator+<S>(const S&, const S&);

This is ill-formed according to the standard, since operator+ is not a template-name.

Suggested resolution:

I think the right rule is

John Spicer adds that there's some question about whether conversion functions should be included, as they cannot have template argument lists.

Notes from 4/02 meeting:

If the change is made as a syntax change, we'll need a semantic restriction to avoid operator+<int> as a class. Clark Nelson will work on a compromise proposal -- not the minimal change to the syntax proposed, not the maximal change either.

Clark Nelson (April 2003):

The proposed solution (adding operator-function-id as an alternative for template-name) would have a large impact on the language described by the grammar. Specifically, for example, operator+<int> would become a syntactically valid class-name.

On the other hand, a change with (I believe) exactly the desired effect on the language accepted, would be to modify operator-function-id itself:

(Steve Adamczyk: this change was already made by issue 38 and is in TC1.)

Then there is the first sentence of 14.2  temp.names paragraph 3:

After name lookup (3.4  basic.lookup) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.

This description seems to be adequate for names of class templates. As far as I can tell, the only ambiguity it resolves is from something that starts with new X <, in the scope of a class template X. But as far as I can tell is already inadequate for names of function templates, and is even worse for operator function templates.

Probably < should always be interpreted as introducing a template-argument-list if any member of the overload set is a function template. After all, function pointers are very rarely compared for ordering, and it's not clear what other rule might be workable.

I'm inclined to propose the simplest rule possible for operator-function-ids: if one is followed by <, then what follows is interpreted as a template-argument-list, unconditionally. Of course, if no template for that operator has been declared, then there's an error.

Also, note that if the operator in question is < or <<, it is possible to run into a problem similar to the famous >> nested template argument list closing delimiter problem. However, since in this case (at least) one of the < characters has a radically different interpretation than the other, and for other reasons as well, this is unlikely to be nearly as much of a practical problem as the >> problem.

Notes from April 2003 meeting:

We felt that the operator functions should not be special-cased. They should be treated like any other name.

September 2003:

Clark Nelson has provided the changes in N1490=03-0073.

Notes from October 2003 meeting:

We reviewed Clark Nelson's N1490. Clark will revise it and introduce a new syntax term for an identifier or the name of an operator function.

Notes from the April, 2005 meeting:

The CWG suggested a new approach to resolving this issue: the existing term template-id will be renamed to class-template-id, the term template-id will be defined to include operator functions with template arguments, and any current uses of template-id (such as in the definition of elaborated-type-specifier) where an operator function is not appropriate will be changed to refer to class-template-id.

Proposed resolution (April, 2006):

As specified in document J16/05-0156 = WG21 N1896, except that:

  1. In change 9 (3.4.5  basic.lookup.classref), omit the change from “entire postfix-expression” to “nested-name-specifier.”

  2. In change a (3.4.3.1  class.qual paragraph 1, third bullet), omit the change from “entire postfix-expression” to “qualified-id.”

  3. In change b (3.4.3.2  namespace.qual paragraph 1), omit the change from “entire postfix-expression” to “qualified-id.”

(Some of these omitted changes are addressed by issue 562.)

(This resolution also resolves issue 534.)




468. Allow ::template outside of templates

Section: 14.2  temp.names     Status: DR     Submitter: John Spicer     Date: 9 Apr 2004

[Voted into WP at the October, 2006 meeting.]

For the same reasons that issue 382 proposes for relaxation of the requirements on typename, it would make sense to allow the ::template disambiguator outside of templates.

See also issues 11, 30, 96, and 109.

Proposed resolution (October, 2005):

Change 14.2  temp.names paragraph 5 as indicated:

If a name prefixed by the keyword template is not the name of a template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. —end note] Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter, or the use does not appear in the scope of a template. —end note]



372. Is access granted by base class specifiers available in following base class specifiers?

Section: 14.3  temp.arg     Status: DR     Submitter: Clark Nelson     Date: 13 August 2002

[Voted into WP at the October, 2006 meeting.]

I'm not really sure what the standard says about this. Personally, I'd like for it to be ill-formed, but I can't find any words that I can interpret to say so.

  template<class T>
  class X
  {
  protected:
    typedef T Type;
  };
  template<class T>
  class Y
  {
  };
  template<class T,
           template<class> class T1,
           template<class> class T2>
  class Z:
    public T2<typename T1<T>::Type>,
    public T1<T>
  {
  };
  Z<int, X, Y> z;

John Spicer: I don't think the standard really addresses this case. There is wording about access checking of things used as template arguments, but that doesn't address accessing members of the template argument type (or template) from within the template.

This example is similar, but does not use template template arguments.

  class X {
  private:
    struct Type {};
  };
  template <class T> struct A {
    typename T::Type t;
  };
  A<X> ax;

This gets an error from most compilers, though the standard is probably mute on this as well. An error makes sense -- if there is no error, there is a hole in the access checking. (The special rule about no access checks on template parameters is not a hole, because the access is checked on the type passed in as an argument. But when you look up something in the scope of a template parameter type, you need to check the access to the member found.)

The logic in the template template parameter case should be similar: anytime you look up something in a template-dependent class, the member's access must be checked, because it could be different for different template instances.

Proposed Resolution (October 2002):

Change the last sentence of 14.3  temp.arg paragraph 3 from:

For a template-argument of class type, the template definition has no special access rights to the inaccessible members of the template argument type.
to:
For a template-argument that is a class type or a class template, the template definition has no special access rights to the members of the template-argument. [Example:
  template <template <class TT> class T> class A {
    typename T<int>::S s;
  };

  template <class U> class B {
    private:
    struct S { /* ... */ };
  };

  A<B> b;   // ill-formed, A has no access to B::S
-- end example]

Daniel Frey posts on comp.std.c++ in July 2003: I just read DR 372 and I think that the problem presented is not really discussed/solved properly. Consider this example:

  class A {
  protected:
     typedef int N;
  };

  template< typename T >
  class B
  {};

  template< typename U >
  class C : public U, public B< typename U::N >
  {};

  C< A > x;

The question is: If C is derived from A as above, is it allowed to access A::N before the classes opening '{'?

The main problem is that you need to access U's protected parts in C's base-clause. This pattern is common when using policies, Andrei's Loki library was bitten by it as he tried to make some parts of the policies 'protected' but some compilers rejected the code. They were right to reject it, I think it's 11.4  class.friend/2 that applies here and prevents the code above to be legal, although it addresses a different and reasonable example. To me, it seems wrong to reject the code as it is perfectly reasonable to write such stuff. The questions are:

Steve Adamczyk: In other words, the point of the issue is over what range access derived from base class specifiers is granted, and whether any part of that range is the base specifier list itself, either the parts afterwards or the whole base specifier list. (Clark Nelson confirms this is what he was asking with the original question.) Personally, I find it somewhat disturbing that access might arrive incrementally; I'd prefer that the access happen all at once, at the opening brace of the class.

Notes from October 2003 meeting:

We decided it makes sense to delay the access checking for the base class specifiers until the opening brace of the class is seen. In other words, the base specifiers will be checked using the full access available for the class, and the order of the base classes is not significant in that determination. The implementors present all said they already had code to handle accumulation of delayed access checks, because it is already needed in other contexts.

Proposed resolution (October, 2004):

  1. Change the last sentence of 14.3  temp.arg paragraph 3 as indicated:

    For a template-argument of that is a class type or a class template, the template definition has no special access rights to the inaccessible members of the template argument type template-argument. [Example:
        template <template <class TT> class T> class A {
           typename T<int>::S s;
        };
    
        template <class U> class B {
           private:
           struct S { /* ... */ };
        };
    
        A<B> b;   // ill-formed, A has no access to B::S
    

    end example]

  2. Insert the following as a new paragraph after the final paragraph of 11  class.access:

    The access of names in a class base-specifier-list are checked at the end of the list after all base classes are known. [Example:

        class A {
          protected:
             typedef int N;
        };
    
        template<typename T> class B {};
    
        template<typename U> class C : public B<typename U::N>, public U {};
    
        C<A> x;  // OK: A is a base class so A::N in B<A::N> is accessible
    

    end example]

Notes from the April, 2005 meeting:

The 10/2004 resolution is not sufficient to implement the CWG's intent to allow these examples: clause 11  class.access paragraph 1 grants protected access only to “members and friends” of derived classes, not to their base-specifiers. The resolution needs to be extended to say either that access in base-specifiers is determined as if they were members of the class being defined or that access is granted to the class as an entity, including its base-specifiers. See also issue 500, which touches on the same issue and should be resolved in the same way.

Proposed resolution (October, 2005):

  1. Change the second bullet of 11  class.access paragraph 1 as indicated:

  2. Change 11  class.access paragraph 2 as indicated:

    A member of a class can also access all the names declared in the class of which it is a member to which the class has access.
  3. Change 11  class.access paragraph 6 as indicated:

    All access controls in clause 11  class.access affect the ability to access a class member name from a particular scope. The access control for names used in the definition of a class member that appears outside of the member's class definition is done as if the entire member definition appeared in the scope of the member's class. For purposes of access control, the base-specifiers of a class and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class. In particular...
  4. Change the example and commentary in 11  class.access paragraphs 6-7 as indicated:

    [Example:

        class A {
          typedef int I; // private member
          I f();
          friend I g(I);
          static I x;
        protected:
          struct B { };
        };
    
        A::I A::f () { return 0; }
        A::I g(A::I p = A::x);
        A::I g(A::I p) { return 0; }
        A::I A::x = 0;
    
        struct D: A::B, A { };
    
    Here, all the uses of A::I are well-formed because A::f and A::x are members of class A and g is a friend of class A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of class A. Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so access checking of base-specifiers must be deferred until the entire base-specifier-list has been seen.end example]
  5. In 11.4  class.friend paragraph 2, replace the following text:

    Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in declarations of members of the befriended class. [Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,

        class A {
          class B { };
          friend class X;
        };
        class X: A::B {     // ill-formed: A::B cannot be accessed
                            // in the base-clause for X
          A::B mx;          // OK: A::B used to declare member of X
          class Y: A::B {   // OK: A::B used to declare member of X
            A::B my;        // ill-formed: A::B cannot be accessed
                            // to declare members of nested class of X
          };
        };
    

    end note]

    with:

    Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in the base-specifiers and member declarations of the befriended class. [Example:

        class A {
          class B { };
          friend class X;
        };
    
        struct X: A::B {   // OK: A::B accessible to friend
          A::B mx;         // OK: A::B accessible to member of friend
          class Y {
            A::B my;       // OK: A::B accessible to nested member of friend
          };
        };
    

    end example]

  6. Change the last sentence of 14.3  temp.arg paragraph 3 as indicated:

    For a template-argument of that is a class type or a class template, the template definition has no special access rights to the inaccessible members of the template argument type. template-argument. [Example:

    
        template <template <class TT> class T> class A {
          typename T<int>::S s;
        };
    
        template <class U> class B {
        private:
          struct S { /* ... */ };
        };
    
        A<B> b;    // ill-formed, A has no access to B::S
    
    

    end example]

  7. Change 9.7  class.nest paragraph 4 as indicated:

    Like a member function, a friend function (11.4  class.friend) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4  class.static), but it and has no special access rights to members of an enclosing class.

(Note: this resolution also resolves issues 494 and 500.)




517. Partial specialization following explicit instantiation

Section: 14.5.4  temp.class.spec     Status: DR     Submitter: John Spicer     Date: 03 May 2005

[Voted into WP at the October, 2006 meeting.]

According to 14.5.4  temp.class.spec paragraph 1,

If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

This leaves the impression that an explicit instantiation of the primary template may precede the declaration of an applicable partial specialization. Is the following example well-formed?

    template<typename T> class X{
        public:
        void foo(){};
    };

    template class X<void *>;

    template<typename T> class X<T*>{
        public:
        void baz();
    };

    void bar() {
        X<void *> x;
        x.foo();
    }

Proposed resolution (October, 2005):

Replace the last sentence of 14.5.4  temp.class.spec paragraph 1:

If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

with:

A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.



515. Non-dependent references to base class members

Section: 14.6.2  temp.dep     Status: DR     Submitter: Mike Miller     Date: 18 Apr 2005

[Voted into WP at the October, 2006 meeting.]

Implementations vary in their treatment of the following code:

    struct A {
      int foo_;
    };
    template <typename T> struct B: public A { };
    template <typename T> struct C: B<T> {
      int foo() {
        return A::foo_;  // #1
      }
    };
    int f(C<int>* p) {
      return p->foo();
    }

According to one analysis, because the expression A::foo_ on line #1 is non-dependent, it must be analyzed in the definition context. It that context, it violates the restrictions of 9.2  class.mem paragraph 10 on how the name of a nonstatic data member of a class can be used and thus should be treated as an error.

On the other hand, the description of the transformation of an id-expression into a class member access expression (9.3.1  class.mfct.nonstatic paragraph 3) does not have any special treatment of templates; when C<int>::foo() is instantiated, the reference to A::foo_ turns out to be to a base class member and is thus transformed into (*this).A::foo_ and is thus not an error.

Proposed resolution (October, 2005):

Change 9.3.1  class.mfct.nonstatic paragraph 3 as indicated:

When an id-expression (5.1  expr.prim) that is not part of a class member access syntax (5.2.5  expr.ref) and not used to form a pointer to member (5.3.1  expr.unary.op) is used in the body of a non-static member function of class X or used in the mem-initializer for a constructor of class X, if name lookup (3.4.1  basic.lookup.unqual) resolves the name in the id-expression to a non-static non-type member of class X or of a base class of X some class C, the id-expression is transformed into a class member access expression (5.2.5  expr.ref) using (*this) (9.3.2  class.this) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note] The member name then refers to the member of the object for which the function is called. Similarly during name lookup...



524. Can function-notation calls to operator functions be dependent?

Section: 14.6.2  temp.dep     Status: DR     Submitter: Daveed Vandevoorde     Date: 19 July 2005

[Voted into WP at the October, 2006 meeting.]

The description of dependent function calls in 14.6.2  temp.dep paragraph 1 applies only to identifiers in postfix-notation function calls and to operator notation calls for operator functions:

In an expression of the form:

where the postfix-expression is an identifier, the identifier denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2  temp.dep.expr). If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name.

It would appear from the related passage in 14.6.4.2  temp.dep.candidate paragraph 1 that the description of postfix-notation function calls should apply to all unqualified-ids that are not template-ids, including operator-function-ids, not just to identifiers:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found...

Proposed resolution (October, 2005):

  1. Change 14.6.2  temp.dep paragraph 1 as indicated:

  2. ...In an expression of the form:

    where the postfix-expression is an identifier unqualified-id but not a template-id, the identifier unqualified-id denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2  temp.dep.expr)...

  3. Change 14.6.4.2  temp.dep.candidate paragraph 1 as indicated:

  4. For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (3.4.1  basic.lookup.unqual, 3.4.2  basic.lookup.argdep) except that...

(See also issue 561.)




522. Array-to-pointer decay in template argument deduction

Section: 14.8.2.1  temp.deduct.call     Status: DR     Submitter: Daveed Vandevoorde     Date: 3 June 2005

[Voted into WP at the October, 2006 meeting.]

Consider the following example:

    char* cmdline3_[1] = {};

    template<class charT>
    void func(const charT* const argv[]) {}

    int main()
    {
        func(cmdline3_);
    }

In terms of the process described in 14.8.2.1  temp.deduct.call, P is const charT* const * and A is char*[1]. According to the first bullet in paragraph 2, the type used in deduction is not A but “the pointer type produced by the array-to-pointer standard conversion.”

According to paragraph 4,

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

In this example, the deduced A is not identical to the transformed A, because the deduced A has additional cv-qualification, so the three exceptions must be examined to see if they apply. The only one that might apply is the second bullet of paragraph 4:

However, A is not a pointer type but an array type; this provision does not apply and deduction fails.

It has been argued that the phrase “after the type A is transformed as described above” should be understood to apply to the A in the three bullets of paragraph 4. If that is the intent, the wording should be changed to make that explicit.

Proposed resolution (October, 2005):

Add the indicated words to 14.8.2.1  temp.deduct.call paragraph 4:

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:




526. Confusing aspects in the specification of non-deduced contexts

Section: 14.8.2.5  temp.deduct.type     Status: DR     Submitter: Mike Miller     Date: 25 July 2005

[Voted into WP at the October, 2006 meeting.]

14.8.2.5  temp.deduct.type paragraph 5 reads:

The non-deduced contexts are:

There are two problems with this list:

  1. The last bullet is redundant with the second bullet. This appears to have been the result of applying the resolutions of issues 70 and 352 independently instead of in coordination.

  2. The second bullet appears to be contradicted by the statement in paragraph 8 saying that an argument can be deduced if P and A have the forms type[i] and template-name<i>.

    The intent of the wording in bullet 2 appears to have been that deduction cannot be done if the template parameter is a sub-expression of the template argument or array bound expression and that it can be done if it is the complete expression, but the current wording does not say that very clearly. (Similar wording also appears in 14.6.2.1  temp.dep.type paragraph 3 and 14.8.2.5  temp.deduct.type paragraph 14.)

Proposed resolution (October, 2005):

  1. Change 14.8.2.5  temp.deduct.type paragraph 5 as indicated:

  2. The non-deduced contexts are:

  3. Change 14.8.2.5  temp.deduct.type paragraph 14 as indicated:

  4. If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used in an expression a subexpression in the function parameter list, the expression is a non-deduced context as specified above...
  5. Change 14.6.2.1  temp.dep.type paragraph 3 as indicated:

  6. A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression involving that contains the template parameter as a subexpression...



415. Template deduction does not cause instantiation

Section: 14.8.3  temp.over     Status: DR     Submitter: John Spicer     Date: 4 May 2003

[Voted into WP at the October, 2006 meeting.]

Mike Miller: In fact, now that I've looked more closely, that appears not to be the case. (At least, it's not the error I get when I compile his example.) Here's a minimal extract (without the inflammatory using-directive :-) that illustrates what I think is going on:

  template <typename _Iterator>
  struct iterator_traits {
    typedef typename _Iterator::difference_type difference_type;
  };

  template <typename _InputIterator>
  inline typename iterator_traits<_InputIterator>::difference_type
  distance(_InputIterator, _InputIterator);

  double distance(const int&, const int&);

  void f() {
    int i = 0;
    int j = 0;
    double d = distance(i, j);
  }

What happens is that iterator_traits<int> is instantiated as part of type deduction for the function template distance, and the instantiation fails. (Note that it can't be instantiation of distance<int>, as I had originally posited, because in this case only a declaration, not a definition, of that template is in scope.)

John Spicer: Yes, I believe that is what is going on.

Mike Miller: I seem to recall that there was some discussion of questions related to this during the core meetings in Oxford. I think Steve Adamczyk said something to the effect that it's infeasible to suppress all instantiation errors during template type deduction and simply call any such errors a deduction failure. (I could be misremembering, and I could be misapplying that comment to this situation.)

John Spicer: Regardless of other conditions in which this may apply, I don't think it would be reasonable for compilers to have to do "speculative instantiations" during template argument deduction. One class instantiation could kick off a series of other instantiations, etc.

Mike Miller: I don't see anything in the Standard that tells me whether it's legitimate or not to report an error in this case. I hope John or another template expert can enlighten me on that.

John Spicer: My opinion is that, because this case is not among those enumerated that cause deduction failure (rather than being ill-formed) that reporting an error is the right thing to do.

Mike Miller: I am still interested, though, in the question of why 14.8.3  temp.over says that viable function template specializations are instantiated, even if they are not selected by overload resolution.

John Spicer: I believe the wording in 14.8.3  temp.over is incorrect. I researched this and found that a change was made during the clause 14 restructuring that was incorporated in March of 1996. The prior wording was "the deduced template arguments are used to generate a single template function". This was changed to "deduced template arguments are used to instantiate a single function template specialization". I believe this resulted from what was basically a global replace of "generate" with "instantiate" and of "template function" with "function template specialization". In this case, the substitution changed the meaning. This paragraph needs reworking.

Proposed resolution (April, 2006):

Change 14.8.3  temp.over paragraph 1 as indicated:

...For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to instantiate synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the function templates instantiated in this way synthesized declarations and all of the non-template overloaded functions of the same name. The function template specializations synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3  over.match.best.



370. Can #include <...> form be used other than for standard C++ headers?

Section: 16.2  cpp.include     Status: DR     Submitter: Beman Dawes     Date: 01 August 2002

[Voted into WP at the October, 2006 meeting.]

The motivation for this issue is a desire to write portable programs which will work with any conforming implementation.

The C++ Standard (16.2  cpp.include) provides two forms of #include directives, with the <...> form being described (16.2  cpp.include paragraph 2) as "for a header", and the "..." form (16.2  cpp.include paragraph 3) as for "the source file" identified between the delimiters. When the standard uses the term "header", it often appears to be limiting the term to apply to the Standard Library headers only. Users of the standard almost always use the term "header" more broadly, to cover all #included source files, but particularly those containing interface declarations.

Headers, including source files, can be categorized according to their origin and usage:

  1. C++ Standard Library headers (which aren't necessarily files).
  2. Other standard libraries such as the POSIX headers.
  3. Operating system API's such as windows.h.
  4. Third party libraries, such as Boost, ACE, or commercial offerings.
  5. Organization-wide "standard" header files, such as a company's config.hpp.
  6. A project's "public" header files, often shared by all developers working on the same project.
  7. A project or user's "private", "local", or "detail" headers, in the same directory or sub-directory as the compilation unit.

Existing practice varies widely, but it is fairly easy to find users advocating:

Do any of the practices A, B, or C result in programs which can be rejected by a conforming implementation?

The first defect is that readers of the standard have not been able to reach consensus on the answers to the above question.

A second possible defect is that if A, B, or C can be rejected by a conforming implementation, then the standard should be changed because would mean there is a wide variance between the standard and existing practice.

Matt Austern: I really only see two positions:

  1. Implementations have some unspecified mechanism (copying files to a magic directory, adding a magic flag,...) such that the line #include <foo> results in textual inclusion of the file foo.
  2. Implementations are not required to have any mechanism for getting #include <foo> to perform textual inclusion of an arbitrary file foo. That form is reserved for standard library headers only.

I agree that the standard should clarify which of those two is the case (I imagine it'll hinge on finding one crucual sentence that either says "implementation defined" or "unspecified"), but from the standpoint of portability I don't see much difference between the two. I claim that, with either of those two interpretations, using #include <foo> is already nonportable.

(Of course, I claim that almost anything having to do with headers, including the #include "foo" form, is also nonportable. In practice there's wide variation in how compilers handle paths, especially relative paths.)

Beman Dawes: The whole issue can be resolved by replacing "header" with "header or source file" in 16.2  cpp.include paragraph 2. That will bring the standard into alignment with existing practice by both users and implementations. The "header and/or source file" wording is used at least three other places in the standard where otherwise there might be some confusion.

John Skaller: In light of Andrew Koenig's comments, this doesn't appear to be the case, since the mapping of include names to file names is implementation defined, and therefore source file inclusion cannot be made portable within the ISO C/C++ standards (since that provision obviously cannot be removed).

A possible idea is to create a binding standard, outside the C/C++ ISO Standards, which specifies not only the path lookup mechanism but also the translation from include names to file names. Clearly that is OS dependent, encoding dependent, etc, but there is no reason not to have a binding standard for Unix, Windows, etc, and specify these bindings in such a way that copying directories from one OS to the other can result in programs working on both OS's.

Andy Koenig: An easier solution might be to specify a (presumably unbounded, or bounded only by implementation capacity) collection of header-file names that every implementation must make it possible for programs to access somehow, without specifying exactly how.

Notes from October 2002 meeting:

This was discussed at some length. While there was widespread agreement that such inclusion is inherently implementation-dependent, we agreed to try to add wording that would make it clear that implementations are permitted (but not required) to allow inclusion of files using the <...> form of #include.

Proposed resolution (April, 2005):

Change 16.2  cpp.include paragraph 7 from:

[Example: The most common uses of #include preprocessing directives are as in the following:
    #include <stdio.h>
    #include "myprog.h"
end example]

to:

[Note: Although an implementation may provide a mechanism for making arbitrary source files available to the < > search, in general programmers should use the < > form for headers provided with the implementation, and the " " form for sources outside the control of the implementation. For instance:
    #include <stdio.h>
    #include <unistd.h>
    #include "usefullib.h"
    #include "myprog.h"
end note]

Notes from October, 2005 meeting:

Some doubt was expressed as to whether the benefit of this non-normative clarification outweighs the overall goal of synchronizing clause 16 with the corresponding text in the C99 Standard. As a result, this issue is being left in “review” status to allow further discussion.






Issues with "WP" Status


513. Non-class “most-derived” objects

Section: 1.8  intro.object     Status: WP     Submitter: Marc Schoolderman     Date: 20 Mar 2005

[Voted into WP at April, 2006 meeting.]

The standard uses “most derived object” in some places (for example, 1.3.3  defns.dynamic.type, 5.3.5  expr.delete) to refer to objects of both class and non-class type. However, 1.8  intro.object only formally defines it for objects of class type.

Possible fix: Change the wording in 1.8  intro.object paragraph 4 from

an object of a most derived class type is called a most derived object

to

an object of a most derived class type, or of non-class type, is called a most derived object

Proposed resolution (October, 2005):

Add the indicated words to 1.8  intro.object paragraph 4:

If a complete object, a data member (9.2  class.mem), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type, or of a non-class type, is called a most derived object.



362. Order of initialization in instantiation units

Section: 2.1  lex.phases     Status: WP     Submitter: Mark Mitchell     Date: 2 July 2002

[Voted into WP at March 2004 meeting.]

Should this program do what its author obviously expects? As far as I can tell, the standard says that the point of instantiation for Fib<n-1>::Value is the same as the point of instantiation as the enclosing specialization, i.e., Fib<n>::Value. What in the standard actually says that these things get initialized in the right order?

  template<int n>
  struct Fib { static int Value; };

  template <>
  int Fib<0>::Value = 0;

  template <>
  int Fib<1>::Value = 1;

  template<int n>
  int Fib<n>::Value = Fib<n-1>::Value + Fib<n-2>::Value;

  int f ()
  {
    return Fib<40>::Value;
  }

John Spicer: My opinion is that the standard does not specify the behavior of this program. I thought there was a core issue related to this, but I could not find it. The issue that I recall proposed tightening up the static initialization rules to make more cases well defined.

Your comment about point of instantiation is correct, but I don't think that really matters. What matters is the order of execution of the initialization code at execution time. Instantiations don't really live in "translation units" according to the standard. They live in "instantiation units", and the handling of instantiation units in initialization is unspecified (which should probably be another core issue). See 2.1  lex.phases paragraph 8.

Notes from October 2002 meeting:

We discussed this and agreed that we really do mean the the order is unspecified. John Spicer will propose wording on handling of instantiation units in initialization.

Proposed resolution (April 2003):

TC1 contains the following text in 3.6.2  basic.start.init paragraph 1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

This was revised by issue 270 to read:

Dynamic initialization of an object is either ordered or unordered. Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data member instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.

This addresses this issue but while reviewing this issue some additional changes were suggested for the above wording:

Dynamic initialization of an object is either ordered or unordered. Definitions of explicitly specialized Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.



261. When is a deallocation function "used?"

Section: 3.2  basic.def.odr     Status: WP     Submitter: Mike Miller     Date: 7 Nov 2000

[Moved to DR at October 2002 meeting.]

3.2  basic.def.odr paragraph 2 says that a deallocation function is "used" by a new-expression or delete-expression appearing in a potentially-evaluated expression. 3.2  basic.def.odr paragraph 3 requires only that "used" functions be defined.

This wording runs afoul of the typical implementation technique for polymorphic delete-expressions in which the deallocation function is invoked from the virtual destructor of the most-derived class. The problem is that the destructor must be defined, because it's virtual, and if it contains an implicit reference to the deallocation function, the deallocation function must also be defined, even if there are no relevant new-expressions or delete-expressions in the program.

For example:

        struct B { virtual ~B() { } };

        struct D: B {
            void operator delete(void*);
            ~D() { }
        };

Is it required that D::operator delete(void*) be defined, even if no B or D objects are ever created or deleted?

Suggested resolution: Add the words "or if it is found by the lookup at the point of definition of a virtual destructor (12.4  class.dtor)" to the specification in 3.2  basic.def.odr paragraph 2.

Notes from 04/01 meeting:

The consensus was in favor of requiring that any declared non-placement operator delete member function be defined if the destructor for the class is defined (whether virtual or not), and similarly for a non-placement operator new if a constructor is defined.

Proposed resolution (10/01):

In 3.2  basic.def.odr paragraph 2, add the indicated text:

An allocation or deallocation function for a class is used by a new expression appearing in a potentially-evaluated expression as specified in 5.3.4  expr.new and 12.5  class.free. A deallocation function for a class is used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5  expr.delete and 12.5  class.free. A non-placement allocation or deallocation function for a class is used by the definition of a constructor of that class. A non-placement deallocation function for a class is used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (12.4  class.dtor). [Footnote: An implementation is not required to call allocation and deallocation functions from constructors or destructors; however, this is a permissible implementation technique.]




289. Incomplete list of contexts requiring a complete type

Section: 3.2  basic.def.odr     Status: WP     Submitter: Mike Miller     Date: 25 May 2001

[Moved to DR at October 2002 meeting.]

3.2  basic.def.odr paragraph 4 has a note listing the contexts that require a class type to be complete. It does not list use as a base class as being one of those contexts.

Proposed resolution (10/01):

In 3.2  basic.def.odr paragraph 4 add a new bullet at the end of the note as the next-to-last bullet:




433. Do elaborated type specifiers in templates inject into enclosing namespace scope?

Section: 3.3.1  basic.scope.pdecl     Status: WP     Submitter: Daveed Vandevoorde     Date: 2 September 2003

[Voted into WP at March 2004 meeting.]

Consider the following translation unit:

  template<class T> struct S {
    void f(union U*);  // (1)
  };
  template<class T> void S<T>::f(union U*) {}  // (2)
  U *p;  // (3)

Does (1) introduce U as a visible name in the surrounding namespace scope?

If not, then (2) could presumably be an error since the "union U" in that definition does not find the same type as the declaration (1).

If yes, then (3) is OK too. However, we have gone through much trouble to allow template implementations that do not pre-parse the template definitions, but requiring (1) to be visible would change that.

A slightly different case is the following:

  template<typename> void f() { union U *p; }
  U *q;  // Should this be valid?

Notes from October 2003 meeting:

There was consensus that example 1 should be allowed. (Compilers already parse declarations in templates; even MSVC++ 6.0 accepts this case.) The vote was 7-2.

Example 2, on the other hand, is wrong; the union name goes into a block scope anyway.

Proposed resolution:

In 3.3.1  basic.scope.pdecl change the second bullet of paragraph 5 as follows:

for an elaborated-type-specifier of the form
   class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest non-class, non-function-prototype scope that contains the declaration. [Note: These rules also apply within templates.] [Note: ...]



432. Is injected class name visible in base class specifier list?

Section: 3.3.6  basic.scope.class     Status: WP     Submitter: Daveed Vandevoorde     Date: