Document number:  PL22.16/11-0006 = WG21 N3236
Date:  2011-02-28
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 Active Issues, Revision 75


This document contains the C++ core language issues on which the Committee (J16 + WG21) has not yet acted, that is, issues with status "Ready," "Tentatively Ready," "Review," "Drafting," and "Open."

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:

Section references in this document reflect the section numbering of document PL22.16/10-0215 = WG21 N3225.

The purpose of these documents is to record the disposition of issues that have come before the Core Language Working Group of the ANSI (INCITS PL22.16) and ISO (WG21) C++ Standard Committee.

Some issues represent potential defects in the ISO/IEC IS 14882:2003 document and corrected defects in the earlier ISO/IEC 14882:1998 document; others refer to text in the working draft for the next revision of the C++ language, informally known as C++0x, and not to any Standard text. Issues are not necessarily formal ISO Defect Reports (DRs). While some issues will eventually be elevated to DR status, others will be disposed of in other ways. (See Issue Status below.)

The most current public version of this document can be found at http://www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2003, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.

Information regarding how to obtain a copy of the C++ Standard, join the Standard Committee, or submit an issue can be found in the C++ FAQ at http://www.comeaucomputing.com/csc/faq.html. Public discussion of the C++ Standard and related issues occurs on newsgroup comp.std.c++.


Revision History

Issue status

Issues progress through various statuses as the Core Language Working Group and, ultimately, the full PL22.16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in these documents by their status. Issues have one of the following statuses:

Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.

Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.

Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.

Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.

Tentatively Ready: Like "ready" except that the resolution was produced and approved by a subset of the working group membership between meetings. Persons not participating in these betwee-meeting activities are encouraged to review such resolutions carefully and to alert the working group with any problems that may be found.

DR: The full Committee has approved the item as a proposed defect report. The Proposed Resolution in an issue with this status reflects the best judgment of the Committee at this time regarding the action that will be taken to remedy the defect; however, the current wording of the Standard remains in effect until such time as a Technical Corrigendum or a revision of the Standard is issued by ISO.

TC1: A DR issue included in Technical Corrigendum 1. TC1 is a revision of the Standard issued in 2003.

CD1: A DR issue not resolved in TC1 but included in Committee Draft 1. CD1 was advanced for balloting at the September, 2008 WG21 meeting.

CD2: A DR issue not resolved in CD1 but included in the Final Committee Draft advanced for balloting at the March, 2010 WG21 meeting.

WP: A DR issue whose resolution is reflected in the current Working Paper. The Working Paper is a draft for a future version of the Standard.

Dup: The issue is identical to or a subset of another issue, identified in a Rationale statement.

NAD: The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.

Extension: The working group has reached consensus that the issue is not a defect in the Standard but is a request for an extension to the language. The working group expresses no opinion on the merits of an issue with this status; however, the issue will be maintained on the list for possible future consideration as an extension proposal.

Concepts: The issue relates to the “Concepts” proposal that was removed from the working paper at the Frankfurt (July, 2009) meeting and hence is no longer under consideration.


Issues with "Ready" Status


1111. Remove dual-scope lookup of member template names

Section: 3.4.5  [basic.lookup.classref]     Status: ready     Submitter: US     Date: 2010-08-02

N3092 comment US 23

According to 3.4.5 [basic.lookup.classref] paragraph 1,

In a class member access expression (5.2.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

This makes the following ill-formed:

    #include <set>
    using std::set;
    struct X {
      template <typename T> void set(const T& value);
    };
    void foo() {
      X x;
      x.set<double>(3.2);
    }

That's confusing and unnecessary. The compiler has already done the lookup in X's scope, and the obviously-correct resolution is that one, not the identifier from the postfix-expression's scope. Issue 305 fixed a similar issue for destructor names but missed member functions.

Suggested resolution: Delete the end of paragraph 1, starting with “If the lookup in the class...” and including all three bullets.

Proposed resolution (November, 2010):

  1. Change 3.4.3.1 [class.qual] paragraph 1 bullet 2 as follows:

  2. Change 3.4.5 [basic.lookup.classref] paragraph 1 as follows:

  3. In a class member access expression (5.2.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

  4. Change 3.4.5 [basic.lookup.classref] paragraph 4 as follows:

  5. If the id-expression in a class member access is a qualified-id of the form

        class-name-or-namespace-name::...
    

    the class-name-or-namespace-name following the . or -> operator is looked up both in the context of the entire postfix-expression and in the scope of the class of the object expression. If the name is found only in the scope of the class of the object expression, the name shall refer to a class-name. If the name is found only in the context of the entire postfix-expression, the name shall refer to a class-name or namespace-name. If the name is found in both contexts, the class-name-or-namespace-name shall refer to the same entity. first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression. [Note: See 3.4.3 [basic.lookup.qual], which describes the lookup of a name before ::, which will only find a type or namespace name. —end note]

  6. Change 3.4.5 [basic.lookup.classref] paragraph 7 as follows:

  7. If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression). is first looked up in the class of the object expression and the name, if found and denotes a type, is used. Otherwise it is looked up in the context of the entire postfix-expression and the name shall denote a type. [Example:

      struct A { };
      namespace N {
        struct A {
          void g() { }
          template <class T> operator T();
        };
      }
    
      int main() {
        N::A a;
        a.operator A();    // calls N::A::operator N::A
      }
    

    end example]




1187. Problems in initialization example

Section: 3.6.2  [basic.start.init]     Status: ready     Submitter: Jason Merrill     Date: 2010-08-31

The note in 3.6.2 [basic.start.init] paragraph 3 contains the following example:

  inline double fd() { return 1.0; }
  extern double d1;
  double d2 = d1;     // unspecified:
                      // may be statically initialized to 0.0 or
                      // dynamically initialized to 1.0
  double d1 = fd();   // may be initialized statically to 1.0

The comment for d2 overlooks the third possibility: if both d1 and d2 are dynamically initialized, d2 will be initialized to 0.

Proposed resolution (November, 2010):

Change the comments in the example in 3.6.2 [basic.start.init] paragraph 3 as follows:

  inline double fd() { return 1.0; }
  extern double d1;
  double d2 = d1;     // unspecified:
                      // may be statically initialized to 0.0 or
                      // dynamically initialized to 1.0 to 0.0 if d1 is dynamically initialized, or 1.0 otherwise
  double d1 = fd();   // may be initialized statically or dynamically to 1.0

(The note should also be in running text following the bulleted list instead of appearing as a bulleted item, as well.)




981. Constexpr constructor templates and literal types

Section: 3.9  [basic.types]     Status: ready     Submitter: Gabriel Dos Reis     Date: 16 October, 2009

3.9 [basic.types] paragraph 10 requires that a class have at least one constexpr constructor other than the copy constructor in order to be considered a literal type. However, a constexpr constructor template might be instantiated in such a way that the constexpr specifier is ignored (7.1.5 [dcl.constexpr] paragraph 5). It is therefore not known whether a class with a constexpr constructor template is a literal type or not until the constructor template is specialized, which could mean that an example like

    struct IntValue {
      template<typename T>
        constexpr IntValue(T t) : val(t) { }

      constexpr intmax_t get_value() { return val; }
     
     private:
       intmax_t val;
    };

is ill-formed, because it is an error to declare a member function (like get_value()) of a non-literal class to be constexpr (7.1.5 [dcl.constexpr] paragraph 6).

3.9 [basic.types] paragraph 10 should be revised so that either a constexpr constructor or constexpr constructor template allows a class to be a literal type.

Proposed resolution (November, 2010):

Change 3.9 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:

This resolution also resolves issues 1071 and 1198.




1071. Literal class types and trivial default constructors

Section: 3.9  [basic.types]     Status: ready     Submitter: Daniel Krügler     Date: 2010-06-02

According to 3.9 [basic.types] paragraph 10, one of the requirements for a literal class type is

This rule has unfortunate consequences. For example, in

    struct A { int x; };
    struct B: A { int y; };

B is a literal type, even though it is impossible to initialize a constant of that type. Conversely, in

    struct C {
        int a, b;
        constexpr C(int x, int y): a(x), b(y) { }
    };
    struct D {
        int x;
        C c;
    };

D is not a literal type, even though it could be initialized as an aggregate.

It would be an improvement to replace the requirement for a trivial default constructor with a requirement that the class be an aggregate.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 981.




1198. Literal types and copy constructors

Section: 3.9  [basic.types]     Status: ready     Submitter: Jason Merrill     Date: 2010-09-16

According to 3.9 [basic.types] paragraph 10, a literal class type has

Is this intended to mean that

    struct A {
       A(const A&) = default;
       A(A&);
    };

is a literal class because it does have a trivial copy constructor even though it also has a non-trivial one? That seems inconsistent with the prohibition of non-trivial move constructors.

My preference would be to resolve this inconsistency by dropping the restriction on non-trivial move constructors. It seems to me that having a trivial copy or move constructor is sufficient, we don't need to prohibit additional non-trivial ones. Actually, it's not clear to me that we need the first condition either; a literal type could be used for singleton variables even if it can't be copied.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 981.




1094. Converting floating-point values to scoped enumeration types

Section: 5.2.9  [expr.static.cast]     Status: ready     Submitter: Daniel Krügler     Date: 2010-07-17

According to 7.2 [dcl.enum] paragraph 10,

An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly.

However, 5.2.9 [expr.static.cast] paragraph 10 says only,

A value of integral or enumeration type can be explicitly converted to an enumeration type.

This omits floating-point values. Presumably unscoped enumeration types are covered by paragraph 7,

The inverse of any standard conversion sequence (Clause 4 [conv]), other than the lvalue-to-rvalue (4.1 [conv.lval]), array-to- pointer (4.2 [conv.array]), function-to-pointer (4.3 [conv.func]), and boolean (4.12 [conv.bool]) conversions, can be performed explicitly using static_cast.

because 4.9 [conv.fpint] paragraph 2 allows an unscoped enumeration value to be implicitly converted to a floating point type. (Although that also covers the integral types, so it's not clear why they would be mentioned specifically in 5.2.9 [expr.static.cast] paragraph 10.) However, this should presumably say “arithmetic” instead of “integral” to match the statement in 7.2 [dcl.enum] paragraph 10.

Proposed resolution (November, 2010):

  1. Change 5.2.9 [expr.static.cast] paragraph 10 as follows:

  2. A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2 [dcl.enum]). Otherwise, the resulting enumeration value is unspecified (and might not be in that range). A value of floating-point type can also be converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration (4.9 [conv.fpint]), and subsequently to the enumeration type.
  3. Add the following footnote to the end of 7.2 [dcl.enum] paragraph 7:

  4. ...If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0. [Footnote: This set of values is used to define promotion and conversion semantics for the enumeration type; it does not exclude an expression of enumeration type from having a value that falls outside this range. —end footnote]
  5. Delete 7.2 [dcl.enum] paragraph 10:

  6. An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.



573. Conversions between function pointers and void*

Section: 5.2.10  [expr.reinterpret.cast]     Status: ready     Submitter: Steve Adamczyk     Date: 13 April 2006

The resolution to issue 195 makes “converting a pointer to a function into a pointer to an object type or vice versa” conditionally-supported behavior. In doing so, however, it overlooked the fact that void is not an “object type” (3.9 [basic.types] paragraph 9). The wording should be amended to allow conversion to and from void* types.

Proposed resolution (November, 2010):

  1. Change 3.7.4.3 [basic.stc.dynamic.safety] paragraphs 1-2 as follows:

  2. A traceable pointer object is

    A pointer value is a safely-derived pointer to a dynamic object only if it has pointer-to-object an object pointer type and it is...

  3. Change 3.9.2 [basic.compound] paragraphs 3-4 as follows:

  4. The type of a pointer to void or a pointer to an object type is called an object pointer type. [Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. —end note] The type of a pointer that can designate a function is called a function pointer type. A pointer to objects of type T is referred to as a “pointer to T.” [Example:...

    Objects of cv-qualified (3.9.3 [basic.type.qualifier]) or cv-unqualified type void* (pointer to void), A pointer to cv-qualified (3.9.3 [basic.type.qualifier]) or cv-unqualified void can be used to point to objects of unknown type. A void* Such a pointer shall be able to hold any object pointer. A cv-qualified or cv-unqualified (3.9.3 [basic.type.qualifier]) An object of type cv void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified cv char*.

  5. Change 4.10 [conv.ptr] paragraph 1 as follows:

  6. ...A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function object pointer or function pointer type...
  7. Change 4.11 [conv.mem] paragraph 2 footnote 58 as follows:

  8. ...Note that a pointer to member is not a pointer to object or a pointer to function an object pointer or a function pointer and...
  9. Change 5.2.10 [expr.reinterpret.cast] paragraphs 6-8 as follows:

  10. A pointer to a function pointer can be explicitly converted to a pointer to a function pointer of a different type...

    A pointer to an An object pointer can be explicitly converted to a pointer to a different object type an object pointer of a different type...

    Converting a pointer to a function into a pointer to an object function pointer to an object pointer type or vice versa is conditionally-supported...

  11. Change the note in 8.3.5 [dcl.fct] paragraph 6 as follows:

  12. [Note: function types are checked during the assignments and initializations of pointer-to-functions, reference-to-functions, and pointer-to-member-functions pointers to functions, references to functions, and pointers to member functions. —end note]
  13. In the “Index of Implementation-defined Behavior,” change the following item as indicated:

  14. converting pointer to function into pointer to object function pointer to object pointer and vice versa

[Drafting note: 5.3.5 [expr.delete] paragraph 1 was not changed, so the operand of delete still cannot be a void*. 13.6 [over.built] paragraph 14 was not changed, so void* pointers still do not get overloads for operator-. 14.1 [temp.param] paragraph 4 was not changed and thus continues to allow only pointers to objects, not object pointers, as non-type template parameters.]

(See also issue 1120.)




1120. reinterpret_cast and void*

Section: 5.2.10  [expr.reinterpret.cast]     Status: ready     Submitter: GB     Date: 2010-08-02

N3092 comment GB 22

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.

See also issue 573.

Proposed resolution (August, 2010):

Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as follows:

A pointer to an object An object pointer can be explicitly converted to a pointer to a different object type an object pointer of a different type.70 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

(Note: this resolution depends on that of issue 573.)




1193. Use of address-constant pointers in constant expressions

Section: 5.19  [expr.const]     Status: ready     Submitter: Daniel Krügler     Date: 2010-09-02

It's not clear whether the current rules for constant expressions allow indirect calls of constexpr functions and constexpr member functions; for example,

    constexpr bool is_negative(int x) { return x < 0; }
    constexpr bool check(int x, bool (*p)(int)) { return p(x); }
    static_assert(check(-2, is_negative), "Error");

If this is to be permitted, there does not seem to be a reason to prohibit equality comparison of pointers to functions or pointers to objects of static storage duration -- these can be tracked as is already done for non-type template parameters.

Proposed resolution (November, 2010):

Change 5.19 [expr.const] paragraph 2 bullet 19 as follows:




1022. Can an enumeration variable have values outside the values of the enumeration?

Section: 7.2  [dcl.enum]     Status: ready     Submitter: Jason Merrill     Date: 2010-01-22

N3092 comment US 31

According to 7.2 [dcl.enum] paragraph 10,

An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.

(There is similar wording in 5.2.9 [expr.static.cast].) Does the phrase “resulting enumeration value” mean that the result, although unspecified, must lie within the range of enumeration values of the enumeration type? Existing practice seems to allow out-of-range values to be preserved if the underlying type is large enough to represent the value. This freedom is important both for efficiency (to avoid having to mask values while storing and/or fetching) and to prevent optimizers from removing code that tests for out-of-range values.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1094.




1135. Explicitly-defaulted non-public special member functions

Section: 8.4.2  [dcl.fct.def.default]     Status: ready     Submitter: FI     Date: 2010-08-02

N3092 comment FI 1

It should be allowed to explicitly default a non-public special member function on its first declaration. It is very likely that users will want to default protected/private constructors and copy constructors without having to write such defaulting outside the class.

Proposed resolution (November, 2010):

  1. Change 8.4.2 [dcl.fct.def.default] paragraphs 1-5 as follows:

  2. A function definition of the form:

    is called an explicitly-defaulted definition. A function that is explicitly defaulted shall

    [Note: This implies that parameter types, return type, and cv-qualifiers must match the hypothetical implicit declaration. —end note]

    An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr, and may have an explicit exception-specification only if it is compatible (15.4 [except.spec]) with the exception-specification on the implicit declaration. If it a function is explicitly defaulted on its first declaration,

    [Note: Such a special member function may be trivial, and thus its accessibility and explicitness should match the hypothetical implicit definition; see below. —end note] [Example:

      struct S {
        constexpr S() = default;                 //ill-formed: implicit S() is not constexpr
        S(int a = 0) = default;                  // ill-formed: default argument
        void operator=(const S&) = default;      // ill-formed: non-matching return type
        ~S() throw(int) = default;               // ill-formed: exception specification doesn't match
      private:
        int i;
        S(S&);                                   // OK: private copy constructor
      };
      S::S(S&) = default;                        // OK: defines copy constructor
    

    end example]

    Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (12.1 [class.ctor] 12.4 [class.dtor], 12.8 [class.copy]), which might mean defining them as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [Note: while an implicitly-declared special member function is inline (Clause 12 [special]), an explicitly-defaulted definition may be non-inline. Non-inline definitions are user-provided, and hence non-trivial (12.1 [class.ctor], 12.4 [class.dtor], 12.8 [class.copy]). This rule enables Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base. —end note]

    [Example:

      struct trivial {
        trivial() = default;
        trivial(const trivial&) = default;
        trivial(trivial&&) = default;
        trivial& operator=(const trivial&) = default;
        trivial& operator=(trivial&&) = default;
        ~trivial() = default;
      };
    
      struct nontrivial1 {
        nontrivial1();
      };
    
      nontrivial1::nontrivial1() = default;           // not inline first declaration
    
      struct nontrivial2 {
        nontrivial2();
      };
      inline nontrivial2::nontrivial2() = default;    // not first declaration
    
      struct nontrivial3 {
        virtual ~nontrivial3() = 0;                   // virtual
      };
      inline nontrivial3::~nontrivial3() = default;   // not first declaration
    

    end example]

  3. Change 12.1 [class.ctor] paragraph 5 as follows:

  4. ...A default constructor is trivial if it is neither not user-provided nor deleted and...
  5. Change 12.4 [class.dtor] paragraph 3 as follows:

  6. ...A destructor is trivial if it is neither not user-provided nor deleted and...
  7. Change 12.8 [class.copy] paragraph 13 as follows:

  8. A copy/move constructor for class X is trivial if it is neither not user-provided nor deleted and...
  9. Change 12.8 [class.copy] paragraph 27 as follows:

  10. A copy/move assignment operator for class X is trivial trivial if it is neither not user-provided nor deleted and...

This resolution also resolves issues 1136, 1137, 1140, 1145, 1149, and 1208.




1136. Explicitly-defaulted explicit constructors

Section: 8.4.2  [dcl.fct.def.default]     Status: ready     Submitter: FI     Date: 2010-08-02

N3092 comment FI 2

It should be allowed to explicitly default an explicit special member function on its first declaration. It is very likely that users will want to default explicit copy constructors without having to write such defaulting outside of the class.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1137. Explicitly-defaulted virtual special member functions

Section: 8.4.2  [dcl.fct.def.default]     Status: ready     Submitter: FI     Date: 2010-08-02

N3092 comment FI 3

It should be allowed to explicitly default a virtual special member function on its first declaration. It is very likely that users will want to default virtual copy assignment operators and destructors without having to write such defaulting outside of the class.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1140. Incorrect redefinition of POD class

Section: 9  [class]     Status: ready     Submitter: US     Date: 2010-08-02

N3092 comment US 50

The class

    struct A { const int i; };

was a POD in C++98, but is not a POD under the FCD rules because it does not have a trivial default constructor. C++0x POD was intended to be a superset of C++98 POD.

Suggested resolution: Change POD to be standard layout and trivially copyable.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1208. Explicit noexcept in defaulted definition

Section: 9.3.1  [class.mfct.non-static]     Status: ready     Submitter: Daniel Krügler     Date: 2010-10-07

It is currently not permitted to specify an exception-specification in a defaulted definition. It would be nice to be able to do so (providing the explicit specification matches the one that would be implicitly supplied) for documentation purposes.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1145. Defaulting and triviality

Section: 12.1  [class.ctor]     Status: ready     Submitter: FI     Date: 2010-08-03

N3092 comment FI 4

What effect does defaulting have on triviality? Related to issue 1135, non-public special members defaulted on their first declaration should retain triviality, because they shouldn't be considered user-provided. Related to issue 1137, defaulted member functions that are virtual should not be considered trivial, but there's no reason why non-virtuals could not be.

Furthermore, a class with a non-public explicitly-defaulted constructor isn't ever trivially constructible under the current rules. If such a class is used as a subobject, the constructor of the aggregating class should be trivial if it can access the non-public explicitly defaulted constructor of a subobject.

Suggested resolution: Change the triviality rules so that a class can have a trivial default constructor if the class has access to the default constructors of its subobjects and the default constructors of the subobjects are explicitly defaulted on first declaration, even if said defaulted constructors are non-public.

See also issue 1149.

Rationale (August, 2010):

The consensus of the CWG was that this change should not be made at this point in the standardization process, but that it might be considered at a later date.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1081. Defaulted destructor and unusable operator delete

Section: 12.4  [class.dtor]     Status: ready     Submitter: Jason Merrill     Date: 2010-06-22

A defaulted destructor should be implicitly defined as deleted if operator delete is deleted or inaccessible.

Proposed resolution (November, 2010):

Change 12.4 [class.dtor] paragraph 3 as follows:

...A defaulted destructor for a class X is defined as deleted if:

A destructor is trivial if...




1080. Confusing relationship between templates and copy constructors

Section: 12.8  [class.copy]     Status: ready     Submitter: Jason Merrill     Date: 2010-06-19

12.8 [class.copy] paragraphs 6-7 currently read,

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments.

A member function template is never instantiated to perform the copy of a class object to an object of its class type. [Example:

    struct S {
        template<typename T> S(T);
        template<typename T> S(T&&);
        S();
    };

    S f();
    const S g;

    void h() {
        S a( f() ); // does not instantiate member template;
                    // uses the implicitly generated move constructor
        S a(g);     // does not instantiate the member template;
                    // uses the implicitly generated copy constructor
    }

These paragraphs were previously a single paragraph, and the second sentence was intended to mean that

    template <class T> A(T):

will never be instantiated to produce A(A). It should not have been split and the example should not have been amended to include move construction.

Lawrence Crowl: I suggest something along the lines of

A member function template is never instantiated to match the signature of an ill-formed constructor.

Proposed resolution (November, 2010):

Merge 12.8 [class.copy] paragraphs 6 and 7 and change the text as follows:

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to perform the copy of a class object to an object of its class type produce such a constructor signature. [Example:

  struct S {
    template<typename T> S(T);
    template<typename T> S(T&&);
    S();
  };

  S f();
  const S g;

  void h() {
    S a( f() ); // does not instantiate member template;
                // uses the implicitly generated move constructor
    S a(g);     // does not instantiate the member template to produce S::S<S>(S);
                // uses the implicitly generated declared copy constructor
}



1149. Trivial non-public copy operators in subobjects

Section: 12.8  [class.copy]     Status: ready     Submitter: FI     Date: 2010-08-03

N3092 comment FI 5

A class with a non-public explicitly-defaulted copy constructor isn't ever trivially copyable under the current rules. If such a class is used as a subobject, the copy constructor of the aggregating class should be trivial if it can access the non-public explicitly defaulted copy constructor of a subobject.

See also issue 1145.

Rationale (August, 2010):

The consensus of the CWG was that this change should not be made at this point in the standardization process, but that it might be considered at a later date.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1151. Overload resolution with initializer-list and non-list constructors

Section: 13.3.1.7  [over.match.list]     Status: ready     Submitter: US     Date: 2010-08-03

N3092 comment US 66

Overload resolution should first look for a viable list constructor, then look for a non-list constructor if no list constructor is viable.

Proposed resolution (August, 2010):

  1. Change 8.5.4 [dcl.init.list] paragraph 3 bullet 5 as follows:

  2. Change 13.3.1.7 [over.match.list] as follows:

  3. When objects of non-aggregate class type T are list-initialized (8.5.4 [dcl.init.list]), overload resolution selects the constructor in two phases as follows, where T is the cv-unqualified class type of the object being initialized:

    For In copy-list-initialization, the candidate functions are all the constructors of T. However, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This differs from other situations (13.3.1.3 [over.match.ctor], 13.3.1.4 [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution.end note]




1073. Merging dynamic-exception-specifications and noexcept-specifications

Section: 15.4  [except.spec]     Status: ready     Submitter: Jason Merrill     Date: 2010-06-02

It is not clear how to handle compatible dynamic-exception-specifications and noexcept-specifications. For example, given

    void f() throw();
    void f() noexcept {
       throw 1;
    }

should we call terminate() or unexpected()? And for

    void g() throw (int);
    void g() noexcept (false) {
       throw 1.0;
    }

should this call unexpected or propagate the exception? Does the order of the declarations (and which is the definition) matter?

Alisdair Meredith:

And what about something like

    struct A { ~A() throw() { } };
    struct B { ~B() noexcept { } };
    struct C: A, B { };

What is the exception specification for C's destructor?

Proposed resolution (November, 2010):

  1. Change 15.4 [except.spec] paragraph 3 as follows:

  2. Two exception-specifications are compatible if:

  3. Add the following note to the end of 15.4 [except.spec] paragraph 9:

  4. Whenever an exception is thrown and the search...

    end example]

    [Note: A function can have multiple declarations with different non-throwing exception-specifications; for this purpose, the one on the function definition is used. —end note]




1167. function-try-blocks for destructors

Section: 15.4  [except.spec]     Status: ready     Submitter: GB     Date: 2010-08-03

N3092 comment GB 46

It is not entirely clear that a function-try-block on a destructor will catch exceptions from a base or member destructor; whether such exceptions might be swallowed with a simple return statement rather than being rethrown; and whether such a clause might be entered multiple times if multiple bases/members throw, or if that is an automatic terminate call.

Proposed resolution (August, 2010):

Change 15 [except] paragraph 4 as follows:

...An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the compound-statement, or — in the case of a destructor — during the destruction of a subobject, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:...

Additional note (October, 2010):

There is a related problem with this wording: it covers only “the execution of the initializer expressions in the ctor-initializer,” when it should also cover execution of base and member constructors, regardless of whether they have initializer expressions in the ctor-initializer or not.

The issue has been moved back to "review" status to allow consideration of amending the proposed resolution to something like

...during the execution of the compound-statement or, if the function is a constructor or destructor, during the initialization or destruction of the class's subobjects, transfers control...

Proposed resolution (November, 2010):

Change 15 [except] paragraph 4 as follows:

A function-try-block associates a handler-seq with the ctor-initializer, if present, and the compound-statement. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the compound-statement or, for constructors and destructors, during the initialization or destruction, respectively, of the class's subobjects, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers.





Issues with "Tentatively Ready" Status


1201. Are deleted and defaulted functions definitions?

Section: 3.1  [basic.def]     Status: tentatively ready     Submitter: Daniel Krügler     Date: 2010-09-20

According to 3.2 [basic.def.odr] paragraph 2,

A declaration is a definition unless it declares a function without specifying the function's body (8.4 [dcl.fct.def]), it contains the extern specifier (7.1.1 [dcl.stc]) or a linkage-specification25 (7.5 [dcl.link]) and neither an initializer nor a function-body...

Because = delete and = default are not forms of function-body, this description does not cover defaulted and deleted functions, even though these declarations are elsewhere referred to as being definitions.

Proposed resolution (January, 2011):

Change the grammar in 8.4.1 [dcl.fct.def.general] paragraph 1 as follows:




1044. Point of declaration for an alias-declaration

Section: 3.3.2  [basic.scope.pdecl]     Status: tentatively ready     Submitter: Daveed Vandevoorde     Date: 2010-03-05

The current wording of 3.3.2 [basic.scope.pdecl] does not specify the point of declaration for an alias-declaration (although it does do so in paragraph 3 for a template alias: “The point of declaration of a template alias immediately follows the identifier for the alias being declared”). One might assume that an alias-declaration would be the same, but it's not clear that that is the right resolution (for either declaration, but especially for the alias-declaration).

An alias-declaration is intended to be essentially a different syntactic form of a typedef declaration (7.1.3 [dcl.typedef] paragraph 2). Placing the point of declaration at the trailing semicolon instead of following the name of the alias would allow more compatibility with the capabilities of typedefs, for instance:

    struct S { };
    namespace N {
        using S = S;
    }

Notes from the November, 2010 meeting:

The CWG agreed that the point of declaration for both template and non-template cases should be at the semicolon.

Proposed resolution (January, 2011):

Change 3.3.2 [basic.scope.pdecl] paragraph 3 as follows:

...The point of declaration of a template an alias or alias template immediately follows the identifier for the alias being declared the type-id to which the alias refers.



1181. What is a “built-in type?”

Section: 3.9  [basic.types]     Status: tentatively ready     Submitter: Barry Hedquist     Date: 2010-08-26

The current draft uses the term “built-in type” several times, but it is not defined anywhere. The Index appears to make it synonymous with “fundamental type,” but the implication of 4 [conv] paragraph 1 is that compound types like pointers should also be considered as “built-in.”

Proposed resolution (January, 2011):

  1. Change 1.8 [intro.object] paragraph 7 as follows:

  2. [Note: C++ provides a variety of built-in fundamental types and several ways of composing new types from existing types (3.9 [basic.types]). —end note]
  3. Change 2.5 [lex.pptoken] as follows:

  4. [Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y are of built-in have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. —end example]
  5. Change 18.3.1.2 [numeric.limits.members] paragraph 58 as follows:

  6. True if the set of values representable by the type is finite.220 [Note: All built-in fundamental types (3.9.1 [basic.fundamental]) are bounded. This member would be false for arbitrary precision types. —end note]
  7. Change 24.2.1 [iterator.requirements.general] paragraph 1 as follows:

  8. ...All input iterators i support the expression *i, resulting in a value of some class, enumeration, or built-in object type T, called the value type of the iterator...
  9. Change 24.2.3 [input.iterators] paragraph 1 as follows:

  10. A class or a built-in pointer type X satisfies the requirements of an input iterator for the value type T if X satisfies the Iterator (24.2.2 [iterator.iterators]) and EqualityComparable (Table 33) requirements and the expressions in Table 107 are valid and have the indicated semantics.
  11. Change 24.2.4 [output.iterators] paragraph 1 as follows:

  12. A class or a built-in pointer type X satisfies the requirements of an output iterator if X if X satisfies the Iterator requirements (24.2.2 [iterator.iterators]) and the expressions in Table 108 are valid and have the indicated semantics.
  13. Change 24.2.5 [forward.iterators] paragraph 1 as follows:

  14. A class or a built-in pointer type X satisfies the requirements of a forward iterator if...
  15. Change 24.2.6 [bidirectional.iterators] paragraph 1 as follows:

  16. A class or a built-in pointer type X satisfies the requirements of a bidirectional iterator if, in addition to satisfying the requirements for forward iterators, the following expressions are valid as shown in Table 110.
  17. Change 24.2.7 [random.access.iterators] paragraph 1 as follows:

  18. A class or a built-in pointer type X satisfies the requirements of a random access iterator if, in addition to satisfying the requirements for bidirectional iterators, the following expressions are valid as shown in Table 111.
  19. Change C.1.2 [diff.basic] section 3.1 as follows:

  20. Rationale: This avoids having different initialization rules for built-in fundamental types and user-defined types.
  21. Change C.1.7 [diff.class] section 9.1 as follows:

  22. ...This new name space definition provides important notational conveniences to C++ programmers and helps making the use of the user-defined types as similar as possible to the use of built-in fundamental types...
  23. Delete the index entry, “built-in type; see fundamental type

    .”

(Note: This resolution assumes that the resolution for issue 572 has been applied, removing “built-in type” from 4 [conv] paragraph 1.)




572. Standard conversions for non-built-in types

Section: 4  [conv]     Status: tentatively ready     Submitter: Jens Maurer     Date: 6 April 2006

4 [conv] paragraph 1 says,

Standard conversions are implicit conversions defined for built-in types.

However, enumeration types (which take part in the integral promotions) and class types (which take part in the lvalue-to-rvalue conversion) are not “built-in” types, so the definition of “standard conversions” is wrong.

Proposed resolution (October, 2006):

Change 4 [conv] paragraph 1 as follows:

Standard conversions are implicit conversions defined for built-in types with built-in meaning...



1091. Inconsistent use of the term “object expression”

Section: 5.5  [expr.mptr.oper]     Status: tentatively ready     Submitter: Tom Plum     Date: 2010-07-10

The description of class member access expressions in 5.2.5 [expr.ref] paragraph 2 defines the terms “object expression” and “pointer expression:”

For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type). For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type).

(Note in passing that the phrase “class object” seems very odd when describing a type.) The rest of that section is based on the equivalence of the expression E1->E2 to (*(E1)).E2 and thus is phrased only in terms of “object expression.” This terminology appears to have been misapplied in other parts of the Standard, using the term “object expression” to refer both to class types and pointers to class types. The most egregious of these is 5.5 [expr.mptr.oper] paragraph 4, describing the operands of a pointer-to-member expression:

The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined.

The dynamic type of the first operand in the ->* case is a pointer type, not a class type, so it cannot “contain the member to which the pointer refers.” Another example is 3.4.5 [basic.lookup.classref], describing the lookup in a class member access expression. The first paragraph uses the term consistently with its use in 5.2.5 [expr.ref], but paragraph 2 reads:

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, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

Paragraph 7 gets it right:

...in the context of the class of the object expression (or the class pointed to by the pointer expression).

Another misapplication of the term occurs in 5.2.2 [expr.call] paragraph 1:

...the call is as a member of the object pointed to or referred to by the object expression (5.2.5 [expr.ref], 5.5 [expr.mptr.oper])... its final overrider (10.3 [class.virtual]) in the dynamic type of the object expression is called. [Note: the dynamic type is the type of the object pointed or referred to by the current value of the object expression...

Here again we have the idea that an object expression can “point to” an object.

Another minor complication is that 5 [expr] paragraph 7 has a separate definition for the (hyphenated) term “object-expression:”

An expression designating an object is called an object-expression.

This term is used several times in the Standard, apparently interchangeably with the non-hyphenated version defined in 5.2.5 [expr.ref]; for example, 5.1.1 [expr.prim.general] paragraph 10 bullet 1 mentions

a class member access (5.2.5 [expr.ref]) in which the object-expression refers to the member's class

using the term defined in 5 [expr] paragraph 7 but linking it with 5.2.5 [expr.ref].

These uses of “object expression” and “object-expression” need to be made consistent, especially the reference in 5.5 [expr.mptr.oper] that implies that the dynamic type of a pointer is that of the complete object to which it points.

Proposed resolution (February, 2011):

  1. Change 3.4.5 [basic.lookup.classref] paragraph 2 as follows:

  2. ...If the type of the object expression is of pointer to scalar type For a pseudo-destructor call (5.2.4 [expr.pseudo]), the unqualified-id is looked up in the context of the complete postfix-expression.
  3. Delete 5 [expr] paragraph 7

  4. An expression designating an object is called an object-expression.
  5. Change 5.1.1 [expr.prim.general] paragraph 10 as follows:

  6. An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  7. Change 5.2.2 [expr.call] paragraph 1 as follows:

  8. ...For a member function call, the postfix expression shall be an implicit (9.3.1 [class.mfct.non-static], 9.4 [class.static]) or explicit class member access (5.2.5 [expr.ref]) whose id-expression is a function member name, or a pointer-to-member expression (5.5 [expr.mptr.oper]) selecting a function member; the call is as a member of the class object pointed to or referred to by the object expression (5.2.5 [expr.ref], 5.5 [expr.mptr.oper])... [Note: the dynamic type is the type of the object pointed or referred to by the current value of the object expression. 12.7 [class.cdtor] describes the behavior of virtual function calls when the object-expression object expression refers to an object under construction or destruction. —end note]
  9. Change 5.2.5 [expr.ref] paragraph 2 as follows:

  10. For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) have complete class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) have pointer to complete class type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 5.2.5 [expr.ref] will address only the first option (dot) [Footnote: Note that (*(E1)) is an lvalue. —end footnote]. In these cases either case, the id-expression shall name a member of the class or of one of its base classes...
  11. Change 5.2.5 [expr.ref] paragraph 3 as follows:

  12. If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 5.2.5 [expr.ref] will address only the first option (dot)66. Abbreviating object-expressionpostfix-expression.id-expression as E1.E2, then the E1 is called the object expression. The type and value category of this expression E1.E2 are determined as follows...
  13. Change 5.5 [expr.mptr.oper] paragraph 3 as follows:

  14. ...The result is an object or a function of the type specified by the second operand. The expression E1->*E2 is converted into the equivalent form (*(E1)).*E2.
  15. Change 5.5 [expr.mptr.oper] paragraph 4 as follows:

  16. The first operand Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of the object expression E1 does not contain the member to which the pointer E2 refers, the behavior is undefined.
  17. Change 5.5 [expr.mptr.oper] paragraph 6 as follows:

  18. ...In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &. In a ->* expression or in a .* expression whose object expression is an lvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &&. The result of a .* expression whose second operand is a pointer to a data member is of the same value category (3.10 [basic.lval]) as its first operand. The result of a .* expression whose second operand is a pointer to a member function is a prvalue. The result of an ->* expression is an lvalue if its second operand is a pointer to data member and a prvalue otherwise. If the second operand is the null pointer to member value (4.11 [conv.mem]), the behavior is undefined.
  19. Change 9.4 [class.static] paragraph 2 as follows:

  20. ...A static member may be referred to using the class member access syntax, in which case the object-expression object expression is evaluated...
  21. Change 12.7 [class.cdtor] paragraph 4 as follows:

  22. ...If the virtual function call uses an explicit class member access (5.2.5) and the object-expression object expression refers to the object under construction...
  23. Change 14.2 [temp.names] paragraph 4 as follows:

  24. When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object or pointer expression of the postfix-expression or...

[Note: although the current text of 3.4.5 [basic.lookup.classref] paragraph 7 mentions the phrase “pointer expression,” that wording will be replaced by issue 1111 or issue 1220 and is thus not addressed here.]




1060. Scoped enumerators in integral constant expressions

Section: 5.19  [expr.const]     Status: tentatively ready     Submitter: Jonathan Caves     Date: 2010-03-21

According to 5.19 [expr.const] paragraph 3,

A constant expression is an integral constant expression if it is of integral or enumeration type. [Note: such expressions may be used as array bounds (8.3.4 [dcl.array], 5.3.4 [expr.new]), as case expressions (6.4.2 [stmt.switch]), as bit-field lengths (9.6 [class.bit]), as enumerator initializers (7.2 [dcl.enum]), and as integral or enumeration non-type template arguments (14.3 [temp.arg]). —end note]

Although there is conceptually a conversion from enumeration type to integral type involved in using an enumerator as an array bound or bit-field length, the normative wording for those uses does not explicitly mention it and simply requires an integral constant expression. Consequently, the current wording permits uses like the following:

    enum class E { e = 10; };
    struct S {
        int arr[E::e];
        int i: E::e;
    };

This seems surprising.

Proposed resolution (February, 2011):

This issue is resolved by the resolution of issue 1197.




1099. Infinite recursion in constexpr functions

Section: 5.19  [expr.const]     Status: tentatively ready     Submitter: Jason Merrill     Date: 2010-08-01

It is not clear what happens when a program violates the limits on constexpr function recursion in a context that does not require a constant expression. For example,

  constexpr int f(int i) { return f(i); }
  const int i = f(1);   // error, undefined behavior, or dynamic initialization?

(Presumably the “within its resource limits” caveat of 1.4 [intro.compliance] paragraph 2 would effectively result in undefined behavior in a context that required a constant expression.)

Notes from the November, 2010 meeting:

The CWG was of mixed opinion as to whether an infinite recursion in a constexpr function should be ill-formed or simply render an expression non-constant.

Proposed resolution (January, 2011):

Add the following bullet in 5.19 [expr.const] paragraph 2:




1100. constexpr conversion functions and non-type template arguments

Section: 5.19  [expr.const]     Status: tentatively ready     Submitter: Jason Merrill     Date: 2010-08-01

According to 14.3.2 [temp.arg.nontype] paragraph 1, one of the possibilities for a template-argument for a non-type, non-template template-parameter is

However, the requirement for such a literal class type is (5.19 [expr.const] paragraph 5):

...that class type shall have a single non-explicit conversion function to an integral or enumeration type and that conversion function shall be constexpr.

Note that this normative requirement for a single conversion function is contradicted by the example in that paragraph, which reads in significant part,

    struct A {
      constexpr A(int i) : val(i) { }
      constexpr operator int() { return val; }
      constexpr operator long() { return 43; }
    private:
      int val;
    };
    template<int> struct X { };
    constexpr A a = 42;
    X<a> x; // OK: unique conversion to int

Proposed resolution (February, 2011):

This issue is resolved by the resolution of issue 1197.




1197. Constexpr arrays

Section: 5.19  [expr.const]     Status: tentatively ready     Submitter: Jason Merrill     Date: 2010-09-08

The requirement in 5.19 [expr.const] that a constant expression cannot contain

effectively eliminates the use of automatic constexpr arrays such as

    void f() {
       constexpr int ar[] = { 1, 2 };
       constexpr int i = ar[1];
    }

There does not seem to be a problem with this kind of usage.

Proposed resolution (February, 2011):

The proposed resolution will be submitted as a separate document.




1054. Lvalue-to-rvalue conversions in expression statements

Section: 6.2  [stmt.expr]     Status: tentatively ready     Submitter: Hans Boehm     Date: 2010-03-16

C and C++ differ in the treatment of an expression statement, in particular with regard to whether a volatile lvalue is fetched. For example,

    volatile int x;
    void f() {
        x;    // Fetches x in C, not in C++
    }

The reason C++ is different in this regard is principally due to the fact that an assignment expression is an lvalue in C++ but not in C. If the lvalue-to-rvalue conversion were applied to expression statements, a statement like

    x = 5;

would write to x and then immediately read it.

It is not clear that the current approach to dealing with the difference in assignment expressions is the only or best approach; it might be possible to avoid the unwanted fetch on the result of an assignment statement without giving up the fetch for a variable appearing by itself in an expression statement.

Proposed resolution (January, 2011):

  1. Add a new paragraph after 5 [expr] paragraph 10:

  2. In some contexts, an expression only appears for its side-effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer (4.2 [conv.array]) and function-to-pointer (4.3 [conv.func]) standard conversions are not applied. The lvalue-to-rvalue conversion (4.1 [conv.lval]) is applied only if the expression is an lvalue of volatile-qualified type and it has one of the following forms:

  3. Change 5.2.9 [expr.static.cast] paragraph 6 as follows:

  4. Any expression can be explicitly converted to type cv void, in which case it becomes a discarded-value expression (Clause 5 [expr]). The expression value is discarded. [Note: however, if the value is in a temporary object (12.2 [class.temporary]), the destructor for that object is not executed until the usual time, and the value of the object is preserved for the purpose of executing the destructor. —end note] 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 not applied to the expression.
  5. Change 5.18 [expr.comma] paragraph 1 as follows:

  6. ...A pair of expressions separated by a comma is evaluated left-to-right; and the value of the left expression is discarded a discarded-value expression (Clause 5 [expr]).83 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 not applied to the left expression. Every value computation...
  7. Change 6.2 [stmt.expr] paragraph 1 as follows:

  8. ...The expression is evaluated and its value is discarded a discarded-value expression (clause 5 [expr]). 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 not applied to the expression. All side effects...



407. Named class with associated typedef: two names or one?

Section: 7.1.3  [dcl.typedef]     Status: tentatively ready     Submitter: Clark Nelson     Date: 31 March 2003

Here's an example:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The big question is, to what declaration does the reference to identifier S actually refer? Is it the S that's declared as a typedef name, or the S that's declared as a class name (or in C terms, as a struct tag)? (In either case, there's clearly only one type to which it could refer, since a typedef declaration does not introduce a new type. But the debugger apparently cares about more than just the identity of the type.)

Here's a classical, closely related example:

  struct stat { ... };
  int stat();
  ... stat( ... ) ...

Does the identifier stat refer to the class or the function? Obviously, in C, you can't refer to the struct tag without using the struct keyword, because it is in a different name space, so the reference must be to the function. In C++, the reference is also to the function, but for a completely different reason.

Now in C, typedef names and function names are in the same name space, so the natural extrapolation would be that, in the first example, S refers to the typedef declaration, as it would in C. But C++ is not C. For the purposes of this discussion, there are two important differences between C and C++

The first difference is that, in C++, typedef names and class names are not in separate name spaces. On the other hand, according to section 3.3.10 [basic.scope.hiding] (Name hiding), paragraph 2:

A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, function, or enumerator name is visible.

Please consider carefully the phrase I have highlighted, and the fact that a typedef name is not the name of an object, function or enumerator. As a result, this example:

  struct stat { ... };
  typedef int stat;

Which would be perfectly legal in C, is disallowed in C++, both implicitly (see the above quote) and explicitly (see section 7.1.3 [dcl.typedef] (The typedef specifier), paragraph 3):

In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.

From which we can conclude that in C++ typedef names do not hide class names declared in the same scope. If they did, the above example would be legal.

The second difference is that, in C++, a typedef name that refers to a class is a class-name; see 7.1.3 [dcl.typedef] paragraph 4:

A typedef-name that names a class is a class-name(9.1). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.5.3) or in the class-head of a class declaration (9), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1, 12.4), the program is ill-formed.

This implies, for instance, that a typedef-name referring to a class can be used in a nested-name-specifier (i.e. before :: in a qualified name) or following ~ to refer to a destructor. Note that using a typedef-name as a class-name in an elaborated-type-specifier is not allowed. For example:

  struct X { };
  typedef struct X X2;
  X x; // legal
  X2 x2; // legal
  struct X sx; // legal
  struct X2 sx2; // illegal

The final relevant piece of the standard is 7.1.3 [dcl.typedef] paragraph 2:

In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

This of course is what allows the original example, to which let us now return:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The question, again is, to which declaration of S does the reference actually refer? In C, it would clearly be to the second, since the first would be accessible only by using the struct keyword. In C++, if typedef names hid class names declared in the same scope, the answer would be the same. But we've already seen that typedef names do not hide class names declared in the same scope.

So to which declaration does the reference to S refer? The answer is that it doesn't matter. The second declaration of S, which appears to be a declaration of a typedef name, is actually a declaration of a class name (7.1.3 [dcl.typedef] paragraph 4), and as such is simply a redeclaration. Consider the following example:

  typedef int I, I;
  extern int x, x;
  void f(), f();

To which declaration would a reference to I, x or f refer? It doesn't matter, because the second declaration of each is really just a redeclaration of the thing declared in the first declaration. So to save time, effort and complexity, the second declaration of each doesn't add any entry to the compiler's symbol table.

Note (March, 2005):

Matt Austern: Is this legal?

    struct A { };
    typedef struct A A;
    struct A* p;

Am I right in reading the standard [to say that this is ill-formed]? On the one hand it's a nice uniform rule. On the other hand, it seems likely to confuse users. Most people are probably used to thinking that 'typedef struct A A' is a null operation, and, if this code really is illegal, it would seem to be a gratuitous C/C++ incompatibility.

Mike Miller: I think you're right. 7.1.3 [dcl.typedef] paragraph 1:

A name declared with the typedef specifier becomes a typedef-name.

7.1.3 [dcl.typedef] paragraph 2:

In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

After the typedef declaration in the example, the name X has been “redefined” — it is no longer just a class-name, it has been “redefined” to be a typedef-name (that, by virtue of the fact that it refers to a class type, is also a class-name).

John Spicer: In C, and originally in C++, an elaborated-type-specifier did not consider typedef names, so “struct X* x” would find the class and not the typedef.

When C++ was changed to make typedefs visible to elaborated-type-specifier lookups, I believe this issue was overlooked and inadvertantly made ill-formed.

I suspect we need add text saying that if a given scope contains both a class/enum and a typedef, that an elaborated type specifier lookup finds the class/enum.

Mike Miller: I'm a little uncomfortable with this approach. The model we have for declaring a typedef in the same scope as a class/enum is redefinition, not hiding (like the “struct stat” hack). This approach seems to assume that the typedef hides the class/enum, which can then be found by an elaborated-type-specifier, just as if it were hidden by a variable, function, or enumerator.

Also, this approach reduces but doesn't eliminate the incompatibility with C. For example:

    struct S { };
    {
        typedef struct S S;
        struct S* p;        // still ill-formed
    }

My preference would be for something following the basic principle that declaring a typedef-name T in a scope where T already names the type designated by the typedef should have no effect on whether an elaborated-type-specifier in that or a nested scope is well-formed or not. Another way of saying that is that a typedef-name that designates a same-named class or enumeration in the same or a containing scope is transparent with respect to elaborated-type-specifiers.

John Spicer: This strikes me as being a rather complicated solution. When we made the change to make typedefs visible to elaborated-type-specifiers we did so knowing it would make some C cases ill-formed, so this does not bother me. We've lived with the C incompatibility for many years now, so I don't personally feel a need to undo it. I also don't like the fact that you have to essentially do the old-style elaborated-type-specifier lookup to check the result of the lookup that found the typedef.

I continue to prefer the direction I described earlier where if a given scope contains both a class/enum and a typedef, that an elaborated-type-specifier lookup finds the class/enum.

Notes from the April, 2005 meeting:

The CWG agreed with John Spicer's approach, i.e., permitting a typedef-name to be used in an elaborated-type-specifier only if it is declared in the same scope as the class or enumeration it names.

Proposed resolution (January, 2011):

Add the following new paragraph after 7.1.3 [dcl.typedef] paragraph 4:

If a typedef specifier is used to redefine in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by an elaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively. [Example:

  struct S;
  typedef struct S S;
  int main() {
    struct S* p; // OK
  }
  struct S {};   // OK

end example]




1199. Deleted constexpr functions

Section: 7.1.5  [dcl.constexpr]     Status: tentatively ready     Submitter: Daniel Krügler     Date: 2010-09-17

The current requirements for constexpr functions do not permit a deleted constexpr function because the definition does not consist of a compound-statement containing just a return statement. However, it could be useful to allow this form in a case where a single piece of code is used in multiple configurations, in some of which the function is constexpr and others deleted; having to update all declarations of the function to remove the constexpr specifier is unnecessarily onerous.

Proposed resolution (January, 2011):

  1. Change 7.1.5 [dcl.constexpr] paragraph 3 as follows:

  2. Change 7.1.5 [dcl.constexpr] paragraph 4 as follows:

  3. The definition of a constexpr constructor In the definition of a constexpr constructor, each of the parameter types shall be a literal type or a reference to a literal type. In addition, either its function-body shall be = delete or it shall satisfy the following constraints:




1240. constexpr defaulted constructors

Section: 8.1  [dcl.name]     Status: tentatively ready     Submitter: Jens Maurer     Date: 2011-02-02

Issue 1199 proposes to add the capability of defining a constexpr special function as deleted. It would be similarly useful to be able to mark a defaulted constructor as constexpr. (It should be noted that the existing text of 12.1 [class.ctor] and the proposed resolution of issue 1224 already allow for implicitly-defined constructors to be implicitly constexpr; this issue simply proposes allowing the explicit use of the constexpr specifier.)

Proposed resolution (February, 2011):

  1. Change 7.1.5 [dcl.constexpr] paragraph 3 as follows:

  2. Change 7.1.5 [dcl.constexpr] paragraph 4 as follows:

  3. In the definition of a constexpr constructor, each of the parameter types shall be a literal type or a reference to a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:

    A trivial copy/move constructor is also a constexpr constructor.

[Note: this resolution assumes that the changes for issue 1199 have been applied.]


938. Initializer lists and array new

Section: 8.5.1  [dcl.init.aggr]     Status: tentatively ready     Submitter: Alisdair Meredith     Date: 12 July, 2009

8.5.1 [dcl.init.aggr] paragraph 4 says,

An initializer-list is ill-formed if the number of initializer-clauses exceeds the number of members or elements to initialize.

However, in a new-expression, the number of elements to be initialized is potentially unknown at compile time. How should an overly-long initializer-list in a new-expression be treated?

Notes from the August, 2010 meeting:

The consensus of the CWG was that this case should throw an exception at runtime.

Proposed resolution (January, 2011):

Change 5.3.4 [expr.new] paragraph 7 as follows:

When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, or if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.2 [new.badlength]).



1030. Evaluation order in initializer-lists used in aggregate initialization

Section: 8.5.1  [dcl.init.aggr]     Status: tentatively ready     Submitter: Scott Meyers     Date: 2010-02-09

The ordering imposed by 8.5.1 [dcl.init.aggr] paragraph 17 applies only to “the full-expressions in an initializer-clause” (i.e., what follows an = in an aggregate initializer); this leaves unspecified the order in which the expressions in an initializer-list (the term used by the braced-init-list form of initializer, with no =) are evaluated.

Notes from the November, 2010 meeting:

The CWG favored guaranteeing the order of evaluation of initializer-clauses appearing in a braced-init-list, regardless of whether the braced-init-list is an aggregate initialization or constructor call.

Proposed resolution (January, 2011):

  1. Delete 8.5.1 [dcl.init.aggr] paragraph 17:

  2. The full-expressions in an initializer-clause are evaluated in the order in which they appear.
  3. Insert the following as a new paragraph between paragraphs 3 and 4 of 8.5.4 [dcl.init.list]

  4. Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3 [temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. —end note]



355. Global-scope :: in nested-name-specifier

Section: 9  [class]     Status: tentatively ready     Submitter: Clark Nelson     Date: 16 May 2002

In looking at a large handful of core issues related to elaborated-type-specifiers and the naming of classes in general, I discovered an odd fact. It turns out that there is exactly one place in the grammar where nested-name-specifier is not immediately preceded by "::opt": in class-head, which is used only for class definitions. So technically, this example is ill-formed, and should evoke a syntax error:

  struct A;
  struct ::A { };

However, all of EDG, GCC and Microsoft's compiler accept it without a qualm. In fact, I couldn't get any of them to even warn about it.

Suggested resolution:

It would simplify the grammar, and apparently better reflect existing practice, to factor the global-scope operator into the rule for nested-name-specifier.

Proposed resolution (February, 2011):

The proposed resolution will be submitted as a separate document.




1207. Type of class member in trailing-return-type

Section: 9.3.1  [class.mfct.non-static]     Status: tentatively ready     Submitter: Jason Merrill     Date: 2010-10-06

Consider the following example:

    struct vector {
        struct iterator { };
        struct const_iterator { };
        iterator begin();
        const_iterator begin() const;
    };
    class block {
        vector v;
        auto end() const -> decltype(v.begin()) { return v.begin(); }
    };

Because the transformation of a member name into a class member access expression (9.3.1 [class.mfct.non-static] paragraph 3) only occurs inside the body of a non-static member function, the type of v in the trailing-return-type is non-const but is const in the return expression, resulting in a type mismatch between the return expression and the return type of the function.

One possibility would be to include the trailing-return-type as being subject to the transformation in 9.3.1 [class.mfct.non-static]. Note, however, that this is currently not in scope at that point (see issue 945).

Notes from the November, 2010 meeting:

The CWG felt that, because this is effectively an implicit parameter, the best approach would be to model its usability on the visibility of parameters: it could be named wherever a parameter of the function is in scope.

Proposed resolution (February, 2011):

  1. Change 5.1.1 [expr.prim.general] paragraph 2 as follows, adding three new paragraphs:

  2. The keyword this names a pointer to the object for which a non-static member function (9.3.2 [class.this]) is invoked or a non-static data member's initializer (9.2 [class.mem]) is evaluated. The keyword this shall be used only inside the body of a non-static member function (9.3 [class.mfct]) of the nearest enclosing class or in a brace-or-equal-initializer for a non-static data member (9.2 [class.mem]). The type of the expression is a pointer to the class of the function or non-static data member, possibly with cv-qualifiers on the class type. The expression is a prvalue.

    If a function-definition or member-declarator declares a member function of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifier-seq and the end of the function-definition or member-declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category is defined within a static member function as it is within a non-static member function). [Note: the type and value category is defined even for the case of a static member function because declaration matching does not occur until the complete declarator is known, and this may be used in the trailing-return-type of the declarator. —end note]

    Otherwise, if a member-declarator declares a non-static data member (9.2 [class.mem]) of a class X, the expression this is a prvalue of type “pointer to X” within the optional brace-or-equal-initializer. It shall not appear elsewhere in the member-declarator.

    The expression this shall not appear in any other context.

    [Example:...

  3. Change 5.1.1 [expr.prim.general] paragraph 10 as follows:

  4. An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  5. Change 9.3.1 [class.mfct.non-static] paragraph 3 as follows:

  6. 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 declaration of a non-static member function of class X, if name lookup (3.4 [basic.lookup]) resolves the name...



696. Use of block-scope constants in local classes

Section: 9.8  [class.local]     Status: tentatively ready     Submitter: Steve Adamczyk     Date: 29 May, 2008

According to 9.8 [class.local] paragraph 1,

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.

This would presumably make both of the members of S2 below ill-formed:

    void test () {
      const int local_const = 7;
      struct S2 {
        int member:local_const;
        void f() { int j = local_const; }
      };
    }

Should there be an exception to this rule for constant values? Current implementations seem to accept the reference to local_const in the bit-field declaration but not in the member function definition. Should they be the same or different?

Notes from the September, 2008 meeting:

The CWG agreed that both uses of local_const in the example above should be accepted. The intent of the restriction was to avoid the need to pass a frame pointer into local class member functions, so uses of local const variables as values should be permitted.

Notes from the October, 2009 meeting:

There was interest in an approach that would allow explicitly-captured constants to appear in constant expressions but also to be “used.” Another suggestion was to have variables captured if they appear in either “use” or “non-use” contexts.

Proposed resolution (February, 2011):

  1. Change 5.1.2 [expr.prim.lambda] paragraph 17 as follows:

  2. Every id-expression that is an odr-use (3.2 [basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [Note: an id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. —end note] If this is captured, each odr-use of this is transformed into an access to the corresponding unnamed data member of the closure type, cast (5.4 [expr.cast]) to the type of this. [Note: the cast ensures that the transformed expression is a prvalue. —end note] [Example:

      void f(const int*);
      void g() {
        const int N = 10;
        [=] {
          int arr[N];    // OK: not an odr-use, refers to automatic variable
          f(&N);         // OK: causes N to be captured; &N points to the
                         // corresponding member of the closure type
        }
      }
    

    end example]

  3. Change 9.8 [class.local] paragraph 1 as follows:
  4. ...Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the shall not odr-use (3.2 [basic.def.odr]) a variable with automatic storage duration from an enclosing scope. [Example:

      int x;
      void f() {
        static int s ;
        int x;
        const int N = 5;
        extern int g q();
    
        struct local {
          int g() { return x; }     // error: odr-use of automatic variable x has automatic storage duration
          int h() { return s; }     // OK
          int k() { return ::x; }   // OK
          int l() { return g q(); } // OK
          int m() { return N; }     // OK: not an odr-use
          int* n() { return &N; }   // error: odr-use of automatic variable N
        };
      }
    
      local* p = 0;                 // error: local not in scope
    

    end example]




1191. Deleted subobject destructors and implicitly-defined constructors

Section: 12.1  [class.ctor]     Status: tentatively ready     Submitter: Jason Merrill     Date: 2010-09-02

Consider the following example:

    struct A {
       A();
       ~A() = delete;
    };

    struct B: A { };
    B* b = new B;

Under the current rules, B() is not deleted, but is ill-formed because it calls the deleted ~A::A() if it exits via an exception after the completion of the construction of A. A deleted subobject destructor should be added to the list of reasons for implicit deletion in 12.1 [class.ctor] and 12.8 [class.copy].

Notes from the November, 2010 meeting:

The CWG agreed that a change was needed, but only if one or more base and/or member constructors are non-trivial.

Proposed resolution (January, 2011):

  1. Add a new bullet to 12.1 [class.ctor] paragraph 5 as follows:

  2. ...A defaulted default constructor for class X is defined as deleted if:

  3. Add a new bullet to 12.8 [class.copy] paragraph 12 as follows:

  4. ...A defaulted copy/move constructor for a class X is defined as deleted (8.4.3 [dcl.fct.def.delete]) if X has:




1096. Missing requirement for template definitions

Section: 14  [temp]     Status: tentatively ready     Submitter: Daniel Krügler     Date: 2010-07-28

The removal of the export keyword inadvertently deleted the text (previously found in 14 [temp] paragraph 8 of the 2003 Standard),

A non-exported template must be defined in every translation unit in which it is implicitly instantiated (14.7.1 [temp.inst]), unless the corresponding specialization is explicitly instantiated (14.7.2 [temp.explicit]) in some translation unit; no diagnostic is required.

This requirement must be reinstated.

Proposed resolution (January, 2011):

Add the following as a new paragraph following 14 [temp] paragraph 5:

A function template, member function of a class template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated (14.7.1 [temp.inst]), unless the corresponding specialization is explicitly instantiated (14.7.2 [temp.explicit]) in some translation unit; no diagnostic is required.



1068. Template aliases with default arguments and template parameter packs

Section: 14.1  [temp.param]     Status: tentatively ready     Submitter: Nikolay Ivchenkov     Date: 2010-03-27

Since there appear to be no restrictions against it, it would appear that default arguments and template parameter packs can be used with template aliases just as with other templates. If that is the case, then, the current wording in 14.1 [temp.param] paragraph 11 requires adjustment:

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a class template is a template parameter pack, it shall be the last template-parameter.

Presumably these restrictions should also apply to template aliases, but as written, they only apply to class templates.

Proposed resolution (January, 2011):

Change 14.1 [temp.param] paragraph 11 as follows:

If a template-parameter of a class template or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter. [Note:...



993. Freedom to perform instantiation at the end of the translation unit

Section: 14.6.4.1  [temp.point]     Status: tentatively ready     Submitter: John Spicer     Date: 6 March, 2009

The intent is that it is a permissible implementation technique to do template instantiation at the end of a translation unit rather than at an actual point of instantiation. This idea is not reflected in the current rules, however.

Proposed resolution (January, 2011):

Change 14.6.4.1 [temp.point] paragraph 7 as follows:

A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template...



1170. Access checking during template argument deduction

Section: 14.8.2  [temp.deduct]     Status: tentatively ready     Submitter: Adamczyk     Date: 2010-08-03

According to 14.8.2 [temp.deduct] paragraph 8,

Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated.

This mimics the way access checking is done in overload resolution. However, experience has shown that this exemption of access errors from deduction failure significantly complicates the Standard library, so this rule should be changed.

Proposed resolution (January, 2011):

Change 14.8.2 [temp.deduct] paragraph 8 as follows:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [Note: Access checking is not done as part of the substitution process. end note] Consequently, when deduction succeeds, an access error could still result when the function is instantiated. Only invalid types...



1218. What is the “currently-handled exception” in a multi-threaded program?

Section: 15.3  [except.handle]     Status: tentatively ready     Submitter: CA     Date: 2010-11-12

N3092 comment CA 5

15.3 [except.handle] paragraph 8 defines the “currently handled exception” as

The exception with the most recently activated handler that is still active

This definition ignores the possibility that an exception might be thrown and caught in another thread during the execution of a handler. Since throw; rethrows the “currently handled exception,” one might conclude that it would be the other thread's exception that would be rethrown instead of the one that activated that handler.

Proposed resolution (January, 2011):

  1. Change 15 [except] paragraph 1 as follows:

  2. Exception handling provides a way of transferring control and information from a point in the execution of a program thread to an exception handler associated with a point previously passed by the execution...
  3. Change 15.1 [except.throw] paragraph 4 as follows:

  4. ...The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way. [Note: An exception thrown by a throw-expression does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see 18.8.5 [propagation] and 30.6 [futures]. —end note]
  5. Change 15.3 [except.handle] paragraph 6 as follows:

  6. If no match is found among the handlers for a try block, the search for a matching handler continues in a dynamically surrounding try block of the same thread.





Issues with "Review" Status


1189. Address of distinct base class subobjects

Section: 1.8  [intro.object]     Status: review     Submitter: Gabriel Dos Reis     Date: 2010-08-31

1.8 [intro.object] paragraph 6 says,

Two distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses.

This formulation leaves open the possibility that two base class subobjects of the same type could have the same address (because one or both might be zero-length base class subobjects).

Proposed resolution (November, 2010):

Change 1.8 [intro.object] paragraph 6 as follows:

Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two distinct objects that are neither not bit-fields nor base class subobjects of zero size shall have distinct addresses, if both have the same type or if not both are base class subobjects of zero size...



1176. Definition of release sequence

Section: 1.10  [intro.multithread]     Status: review     Submitter: CA, GB     Date: 2010-08-10

N3092 comment CA 12
N3092 comment GB 9

The current wording of the standard suggests that release sequences are maximal with respect to sequence inclusion, i.e. that if there are two release operations in the modification order,

mod       mod
rel1----->rel2----->w

then [rel1;rel2;w] is the only release sequence, as the other candidate [rel2;w] is included in it. This interpretation precludes synchronizing with releases which have other releases sequenced-before them. We believe that the intention is actually to define the maximal release sequence from a particular release operation, which would admit both [rel1;rel2;w] and [rel2;w].

Proposed resolution (August, 2010):

Change 1.10 [intro.multithread] paragraph 6 as follows:

A release sequence from a release operation A on an atomic object M is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first operation is a release A, and every subsequent operation




1177. Intra-thread dependency-ordered-before

Section: 1.10  [intro.multithread]     Status: review     Submitter: CA     Date: 2010-08-10

N3092 comment CA 15

The current draft has release/acquire synchronize-with edges only between a release on one thread and an acquire on a different thread, whereas the definition of dependency-ordered-before permits the release and consume to be on the same thread; it seems odd to permit the latter. (At the moment function arguments can't race or sync with each other, but they can be dependency ordered before each other.)

We don't currently have an example in which this makes a real difference, but for symmetry could suggest changing the definition of dependency-ordered-before in 1.10 [intro.multithread].

Proposed resolution (August, 2010):

Change 1.10 [intro.multithread] paragraph 9 as follows:

An evaluation A is dependency-ordered before an evaluation B if

[Note:...




1175. Disambiguating user-defined literals

Section: 2.14.8  [lex.ext]     Status: review     Submitter: Sebastian Gesemann     Date: 2010-08-10

A user-defined literal like 0x123DZ could be parsed either as a hexadecimal-literal of 0x123 and a ud-suffix of DZ or as a hexadecimal-literal of 0x123D and a ud-suffix of Z. There does not appear to be a rule that disambiguates the two possible parses.

Proposed resolution (November, 2010):

Change 2.14.8 [lex.ext] paragraph 1 as follows:

If a token matches both user-defined-literal and another literal kind, it is treated as the latter. [Example: 123_km, 1.2LL, "Hello"s are all user-defined-literals, but 12LL is an integer-literal. —end example] The syntactic nonterminal preceding the ud-suffix in a user-defined-literal is taken to be the longest sequence of characters that could match that nonterminal. [Example: The ud-suffix in 1.0e0X is X, not e0X; in 0x1DZ, the ud-suffix is Z, not DZ. —end example]



758. Missing cases of declarations that are not definitions

Section: 3.1  [basic.def]     Status: review     Submitter: Bjarne Stroustrup     Date: 22 January, 2009

N2800 comment UK 23
N2800 comment UK 24

The list of declarations that are not definitions given in 3.1 [basic.def] paragraph 2 does not mention several plausible candidates: parameter declarations in non-defining function declarations, non-static data members, and template parameters. It might be argued that none of these are declarations (paragraph 1 does not use the syntactic non-terminal declaration but does explicitly refer to clause 7 [dcl.dcl], where that non-terminal is defined). However, the list in paragraph 2 does mention static member declarations, which also are not declarations, so the intent is not clear.

Proposed resolution (November, 2010):

Change 3.1 [basic.def] paragraph 2 as follows:

A declaration is a definition unless it declares a function without specifying the function's body (8.4 [dcl.fct.def]), it contains the extern specifier (7.1.1 [dcl.stc]) or a linkage-specification25 (7.5 [dcl.link]) and neither an initializer nor a function-body, it declares a static data member in a class definition (9.2 [class.mem], 9.4 [class.static]), it is a class name declaration (9.1 [class.name]), it is an opaque-enum-declaration (7.2 [dcl.enum]), it is a template-parameter (14.1 [temp.param]), it is a parameter-declaration (8.3.5 [dcl.fct]) in a function declaration that is not a definition, or it is a typedef declaration (7.1.3 [dcl.typedef]), an alias-declaration (7.1.3 [dcl.typedef]), a using-declaration (7.3.3 [namespace.udecl]), a static_assert-declaration (Clause 7 [dcl.dcl]), an attribute-declaration (Clause 7 [dcl.dcl]), an empty-declaration (Clause 7 [dcl.dcl]), or a using-directive (7.3.4 [namespace.udir]).



712. Are integer constant operands of a conditional-expression “used?”

Section: 3.2  [basic.def.odr]     Status: review     Submitter: Mike Miller     Date: 9 September, 2008

In describing static data members initialized inside the class definition, 9.4.2 [class.static.data] paragraph 3 says,

The member shall still be defined in a namespace scope if it is used in the program...

The definition of “used” is in 3.2 [basic.def.odr] paragraph 1:

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 a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied.

Now consider the following example:

    struct S {
      static const int a = 1;
      static const int b = 2;
    };
    int f(bool x) {
      return x ? S::a : S::b;
    }

According to the current wording of the Standard, this example requires that S::a and S::b be defined in a namespace scope. The reason for this is that, according to 5.16 [expr.cond] paragraph 4, the result of this conditional-expression is an lvalue and the lvalue-to-rvalue conversion is applied to that, not directly to the object, so this fails the “immediately applied” requirement. This is surprising and unfortunate, since only the values and not the addresses of the static data members are used. (This problem also applies to the proposed resolution of issue 696.)

Proposed resolution (November, 2009):

Change 3.2 [basic.def.odr] paragraph 1 as follows:

...An object or non-overloaded function whose name appears as a potentially-evaluated expression x is used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied eventually applied to all lvalue expressions e that could possibly denote that object, where e is a subexpression of the full-expression containing x...

Additional notes (November, 2009):

The proposed wording (like the existing wording) requires that S::a be defined in the following example:

  struct S {
    static const int a = 1;
  };
  void g() {
    S::a;  // no lvalue-to-rvalue conversion
  }

Although this particular example is obviously unimportant, there could be similar cases where a use is buried in a nested conditional and the result eventually discarded, perhaps as the result of a macro expansion. An alternative approach that addresses this point might be something along the lines of

There is no lvalue expression e of which x is a subexpression to which a reference is bound or to which the unary & operator is applied that could possibly denote that object.

One objection to this latter approach is that it would require defining S::a if the expression were something like *&S::a, which would not be the case with the wording in the proposed resolution.




1174. When is a pure virtual function “used?”

Section: 3.2  [basic.def.odr]     Status: review     Submitter: Daniel Krügler     Date: 2010-08-07

According to 3.2 [basic.def.odr] paragraph 2,

A variable or non-overloaded function whose name appears as a potentially-evaluated expression is used... A virtual member function is used if it is not pure.

However, that does not adequately address when a pure virtual function is used or not used. For example,

    struct S {
      virtual void pure1() = 0;
      virtual void pure2() = 0;
    };
    void f(S* p) {
      p->pure1();
      p->S::pure2();
    };

Both pure1 and pure2 satisfy the criterion that their name appears in a potentially-evaluated expression, but pure1 should not be considered “used” (which would require that it be defined); pure2 is “used” and must be defined.

Proposed resolution (November, 2010):

Change 3.2 [basic.def.odr] paragraph 2 as follows:

...A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied... A virtual member function is odr-used if it is not pure. A non-overloaded function whose name appears as a potentially-evaluated expression or a member of a set of candidate functions is odr-used if it is selected by overload resolution when referred to from a potentially-evaluated expression, are odr-used, unless the function is pure and its name is not explicitly qualified. [Note:...



1192. Inadvertent change to ODR and templates

Section: 3.2  [basic.def.odr]     Status: review     Submitter: Daniel Krügler     Date: 2010-09-03

Issue 678 added a bullet to the list in 3.2 [basic.def.odr] paragraph 5, inadvertently removing the second bullet from the reach of the part of that paragraph that reads,

If D is a template and is defined in more than one translation unit, then the last four requirements from the list above shall apply to names from the template's enclosing scope used in the template definition (14.6.3 [temp.nondep]),

In fixing this error, the wording should be recast to be more robust in the face of possible further edits to the list (i.e., not just changing “four” to “five”).

Proposed resolution (November, 2010):

Change 3.2 [basic.def.odr] paragraph 5 as follows:

...If D is a template and is defined in more than one translation unit, then the last four preceding requirements from the list above shall apply both to names from the template's enclosing scope used in the template definition (14.6.3 [temp.nondep]), and also to dependent names at the point of instantiation (14.6.2 [temp.dep])...



554. Definition of “declarative region” and “scope”

Section: 3.3  [basic.scope]     Status: review     Submitter: Gabriel Dos Reis     Date: 29 December 2005

The various uses of the term “declarative region” throughout the Standard indicate that the term is intended to refer to the entire block, class, or namespace that contains a given declaration. For example, 3.3 [basic.scope] paragraph 2 says, in part:

[Example: in

    int j = 24;
    int main()
    {
        int i = j, j;
        j = 42;
    }

The declarative region of the first j includes the entire example... The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }...

However, the actual definition given for “declarative region” in 3.3 [basic.scope] paragraph 1 does not match this usage:

Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.

Because (except in class scope) a name cannot be used before it is declared, this definition contradicts the statement in the example and many other uses of the term throughout the Standard. As it stands, this definition is identical to that of the scope of a name.

The term “scope” is also misused. The scope of a declaration is defined in 3.3 [basic.scope] paragraph 1 as the region in which the name being declared is valid. However, there is frequent use of the phrase “the scope of a class,” not referring to the region in which the class's name is valid but to the declarative region of the class body, and similarly for namespaces, functions, exception handlers, etc. There is even a mention of looking up a name “in the scope of the complete postfix-expression” (3.4.5 [basic.lookup.classref] paragraph 3), which is the exact inverse of the scope of a declaration.

This terminology needs a thorough review to make it logically consistent. (Perhaps a discussion of the scope of template parameters could also be added to section 3.3 [basic.scope] at the same time, as all other kinds of scopes are described there.)

Proposed resolution (November, 2006):

  1. Change 3.3 [basic.scope] paragraph 1 as follows:

  2. Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity a statement, block, function declarator, function-definition, class, handler, template-declaration, template-parameter-list of a template template-parameter, or namespace. In general, each particular name is valid may be used as an unqualified name to refer to the entity of its declaration or to the label only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration...
  3. Change 3.3 [basic.scope] paragraph 3 as follows:

  4. The names declared by a declaration are introduced into the scope in which the declaration occurs declarative region that directly encloses the declaration, except that declaration-statements, function parameter names in the declarator of a function-definition, exception-declarations (3.3.3 [basic.scope.local]), the presence of a friend specifier (11.4 [class.friend]), certain uses of the elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), and using-directives (7.3.4 [namespace.udir]) alter this general behavior.
  5. Change 3.3.3 [basic.scope.local] paragraphs 1-3 and add a new paragraph 4 before the existing paragraph 4 as follows:

  6. A name declared in a block (6.3 [stmt.block]) is local to that block. Its potential scope begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region. The declarative region of a name declared in a declaration-statement is the directly enclosing block (6.3 [stmt.block]). Such a name is local to the block.

    The potential scope declarative region of a function parameter name (including one appearing in the declarator of a function-definition or in a lambda-parameter-declaration-clause) or of a function-local predefined variable in a function definition (8.4 [dcl.fct.def]) begins at its point of declaration. If the function has a function-try-block the potential scope of a parameter or of a function-local predefined variable ends at the end of the last associated handler, otherwise it ends at the end of the outermost block of the function definition. A parameter name is the entire function definition or lambda-expression. Such a name is local to the function definition and shall not be redeclared in the any outermost block of the function definition nor in the outermost block of any handler associated with a function-try-block function-body (including handlers of a function-try-block) or lambda-expression.

    The name in a catch exception-declaration The declarative region of a name declared in an exception-declaration is its entire handler. Such a name is local to the handler and shall not be redeclared in the outermost block of the handler.

    The potential scope of any local name begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region.

  7. Change 3.3.5 [basic.funscope] as indicated:

  8. Labels (6.1 [stmt.label]) have function scope and may be used anywhere in the function in which they are declared except in members of local classes (9.8 [class.local]) of that function. Only labels have function scope.
  9. Change 6.7 [stmt.dcl] paragraph 1 as follows:

  10. A declaration statement introduces one or more new identifiers names into a block; it has the form

    [Note: If an identifier a name introduced by a declaration was previously declared in an outer block, the outer declaration is hidden for the remainder of the block, after which it resumes its force (3.3.10 [basic.scope.hiding]). end note]

[Drafting notes: This resolution deals almost exclusively with the unclear definition of “declarative region.” I've left the ambiguous use of “scope” alone for now. However sections 3.3.x all have headings reading “xxx scope,” but they don't mean the scope of a declaration but the different kinds of declarative regions and their effects on the scope of declarations contained therein. To me, it looks like most of 3.4 should refer to “declarative region” and not to “scope.”

The change to 6.7 fixes an “identifier” misuse (e.g., extern T operator+(T,T); at block scope introduces a name but not an identifier) and removes normative redundancy.]




555. Pseudo-destructor name lookup

Section: 3.4  [basic.lookup]     Status: review     Submitter: Krzysztof Zelechowski     Date: 26 January 2006

The Standard does not completely specify how to look up the type-name(s) in a pseudo-destructor-name (5.2 [expr.post] paragraph 1, 5.2.4 [expr.pseudo]), and what information it does have is incorrect and/or in the wrong place. Consider, for instance, 3.4.5 [basic.lookup.classref] paragraphs 2-3:

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. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

If the unqualified-id is ~type-name, and the type of the object expression is of a class type C (or of pointer to a class type C), the type-name is 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.

There are at least three things wrong with this passage with respect to pseudo-destructors:

  1. A pseudo-destructor call (5.2.4 [expr.pseudo]) is not a “class member access”, so the statements about scalar types in the object expressions are vacuous: the object expression in a class member access is required to be a class type or pointer to class type (5.2.5 [expr.ref] paragraph 2).

  2. On a related note, the lookup for the type-name(s) in a pseudo-destructor name should not be described in a section entitled “Class member access.”

  3. Although the class member access object expressions are carefully allowed to be either a class type or a pointer to a class type, paragraph 2 mentions only a “pointer to scalar type” (disallowing references) and paragraph 3 deals only with a “scalar type,” presumably disallowing pointers (although it could possibly be a very subtle way of referring to both non-class pointers and references to scalar types at once).

The other point at which lookup of pseudo-destructors is mentioned is 3.4.3 [basic.lookup.qual] paragraph 5:

If a pseudo-destructor-name (5.2.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.

Again, this specification is in the wrong location (a pseudo-destructor-name is not a qualified-id and thus should not be treated in the “Qualified name lookup” section).

Finally, there is no place in the Standard that describes the lookup for pseudo-destructor calls of the form p->T::~T() and r.T::~T(), where p and r are a pointer and reference to scalar, respectively. To the extent that it gives any guidance at all, 3.4.5 [basic.lookup.classref] deals only with the case where the ~ immediately follows the . or ->, and 3.4.3 [basic.lookup.qual] deals only with the case where the pseudo-destructor-name contains a nested-name-specifier that designates a scope in which names can be looked up.

See document J16/06-0008 = WG21 N1938 for further discussion of this and related issues, including 244, 305, 399, and 414.

Proposed resolution (June, 2008):

  1. Add a new paragraph following 5.2 [expr.post] paragraph 2 as follows:

  2. When a postfix-expression is followed by a dot . or arrow -> operator, the interpretation depends on the type T of the expression preceding the operator. If the operator is ., T shall be a scalar type or a complete class type; otherwise, T shall be a pointer to a scalar type or a pointer to a complete class type. When T is a (pointer to) a scalar type, the postfix-expression to which the operator belongs shall be a pseudo-destructor call (5.2.4 [expr.pseudo]); otherwise, it shall be a class member access (5.2.5 [expr.ref]).
  3. Change 5.2.4 [expr.pseudo] paragraph 2 as follows:

  4. The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type The type of the expression preceding the dot operator, or the type to which the expression preceding the arrow operator points, is the object type...
  5. Change 5.2.5 [expr.ref] paragraph 2 as follows:

  6. For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) is a class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) is a pointer to a class type. In these cases, the id-expression shall name a member of the class or of one of its base classes.
  7. Add a new paragraph following 3.4 [basic.lookup] paragraph 2 as follows:

  8. In a pseudo-destructor-name that does not include a nested-name-specifier, the type-names are looked up as types in the context of the complete expression.
  9. Delete the last sentence of 3.4.5 [basic.lookup.classref] paragraph 2:

  10. 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, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.



997. Argument-dependent lookup and dependent function template parameter types

Section: 3.4.2  [basic.lookup.argdep]     Status: review     Submitter: James Widman     Date: 6 November, 2009

3.4.2 [basic.lookup.argdep] paragraph 2 excludes dependent parameter types and return types from consideration in determining the associated classes and namespaces of a function template. Presumably this means that an example like

    namespace N {
      template<class T> struct A { };
      void f(void (*)());
    }

    template <class T>
    void g(T, N::A<T>);

    void g();

    int main() {
      f(g);
    }

is ill-formed because the second parameter of the function template g does not add namespace N to the list of associated namespaces. This was probably unintentional.

See also issue 1015.

Notes from the November, 2010 meeting:

The CWG agreed that the rules should be changed to make this example well-formed.

Proposed resolution (November, 2010):

Change 3.4.2 [basic.lookup.argdep] paragraph 2 as follows:

...In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set, i.e., the classes and namespaces associated with its (non-dependent) parameter types and return type. Additionally, if the aforementioned set of overloaded functions is named with a template-id, its associated classes and namespaces are those of its type template-arguments and its template template-arguments.

This resolution also resolves issue 1015.

[Drafting note: It's not clear that we need the inserted text above, because for the example in issue 1015, the type N::S is already represented in the type of the function address, so there is no need to pull it from template arguments. For cases where template parameters are not represented in the function type, it's not clear that we want ADL to reach further.]




1015. Template arguments and argument-dependent lookup

Section: 3.4.2  [basic.lookup.argdep]     Status: review     Submitter: Jason Merrill     Date: 2009-12-24

Currently, according to 3.4.2 [basic.lookup.argdep] paragraph 2, explicit template arguments in a function argument do not contribute to the associated namespaces in a function call, although they plausibly should in an example like the following:

    namespace N {
        struct S { };
        void f(void (*)(S));
    };

    template<typename T> void g(T);

    void h() {
        f(g<N::S>);    // Should find N::f
    }

See also issue 997.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 997.




1190. Operations on non-safely-derived pointers

Section: 3.7.4.3  [basic.stc.dynamic.safety]     Status: review     Submitter: Hans Boehm     Date: 2010-09-01

3.7.4.3 [basic.stc.dynamic.safety] paragraph 4 only prohibits the dereferencing and deallocation of non-safely-derived pointers. This is insufficient. Explicit deallocation of storage is described as rendering invalid all pointers to that storage, with the result that all operations on such a pointer value causes undefined behavior (3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4). The same should be true if the storage pointed to by a non-safely-derived pointer is garbage collected. In particular, the promise of objects having distinct addresses (1.8 [intro.object] paragraph 6) should not apply if one of those objects is designated by a non-safely-derived pointer.

Proposed resolution (November, 2010):

Change 3.7.4.3 [basic.stc.dynamic.safety] paragraph 4 as follows:

...Alternatively, an implementation may have strict pointer safety, in which case, if a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and an invalid pointer value, unless the referenced complete object is of dynamic storage duration and has not previously been declared reachable (20.9.10 [util.smartptr]), the behavior is undefined. [Note: this The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see 3.7.4.2 [basic.stc.dynamic.deallocation]. This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note] It is implementation defined...



597. Conversions applied to out-of-lifetime non-POD lvalues

Section: 3.8  [basic.life]     Status: review     Submitter: Mike Miller     Date: 27 September 2006

An lvalue referring to an out-of-lifetime non-POD class objects can be used in limited ways, subject to the restrictions in 3.8 [basic.life] paragraph 6:

if the original object will be or was of a non-POD class type, the program has undefined behavior if:

There are at least a couple of questionable things in this list. First, there is no “implicit conversion to a reference to a base class,” as assumed by the second bullet. Presumably this is intended to say that the lvalue is bound to a reference to a base class, and the cross-reference should be to 8.5.3 [dcl.init.ref], not to 4.10 [conv.ptr] (which deals with pointer conversions). However, even given that adjustment, it is not clear why it is forbidden to bind a reference to a non-virtual base class of an out-of-lifetime object, as that is just an address offset calculation. (Binding to a virtual base, of course, would require access to the value of the object and thus cannot be done outside the object's lifetime.)

The third bullet also appears questionable. It's not clear why static_cast is discussed at all here, as the only permissible static_cast conversions involving reference types and non-POD classes are to references to base or derived classes and to the same type, modulo cv-qualification; if implicit “conversion” to a base class reference is forbidden in the second bullet, why would an explicit conversion be permitted in the third? Was this intended to refer to reinterpret_cast? Also, is there a reason to allow char types but disallow array-of-char types (which are more likely to be useful than a single char)?

Proposed resolution (March, 2008):

  1. Change 3.8 [basic.life] paragraph 5 as follows:

  2. ...If the object will be or was of a non-trivial class type, the program has undefined behavior if:

  3. Change 3.8 [basic.life] paragraph 6 as follows:

  4. ...if the original object will be or was of a non-trivial class type, the program has undefined behavior if:

[Drafting notes: Paragraph 5 was changed to track the changes to paragraph 6. See also the resolution for issue 658.]




1219. Non-static data member initializers in constant expressions

Section: 3.9  [basic.types]     Status: review     Submitter: Jens Maurer     Date: 2010-11-13

The current treatment of constexpr constructors and constant expressions does not deal with the initializers for non-static data members, which should also be required to be constant expressions.

Proposed resolution (November, 2010):

  1. Change 3.6.2 [basic.start.init] paragraph 2 as follows:

  2. Change 3.9 [basic.types] paragraph 10 as follows (wording assumes the proposed resolution of issue 981)

  3. A type is a literal type if it is:




1055. Permissible uses of void

Section: 3.9.1  [basic.fundamental]     Status: review     Submitter: Nikolay Ivchenkov     Date: 2010-03-17

According to 3.9.1 [basic.fundamental] paragraph 9,

Any expression can be explicitly converted to type cv void (5.4 [expr.cast]). An expression of type void shall be used only as an expression statement (6.2 [stmt.expr]), as an operand of a comma expression (5.18 [expr.comma]), as a second or third operand of ?: (5.16 [expr.cond]), as the operand of typeid, or as the expression in a return statement (6.6.3 [stmt.return]) for a function with the return type void.

First, this is self-contradictory: if “any expression” can be converted to void, why is such a conversion not listed among the acceptable uses of an expression of type void?

Second, presumably an expression of type void can be used as an operand of decltype, but this use is not listed.

Finally, there are several places in the Standard that speak of expressions having a cv-qualified void type (5.16 [expr.cond] paragraph 2, 6.6.3 [stmt.return] paragraph 3). However, an expression of type void is a non-class prvalue, and there are no cv-qualified non-class prvalues (3.10 [basic.lval] paragraph 4).

Proposed resolution (February, 2011):

  1. Change 3.9.1 [basic.fundamental] paragraph 9 as follows:

  2. ...Any expression can be explicitly converted to type cv void (5.4 [expr.cast]). An expression of type void shall be used only as an expression statement (6.2 [stmt.expr]), as an operand of a comma expression (5.18 [expr.comma]), as a second or third operand of ?: (5.16 [expr.cond]), as the operand of typeid or decltype, or as the expression in a return statement (6.6.3 [stmt.return]) for a function with the return type void, or as the operand of an explicit conversion to type cv void.
  3. Change 5.16 [expr.cond] paragraph 2 as follows:

  4. If either the second or the third operand has type (possibly cv-qualified) void, then...
  5. Change 6.6.3 [stmt.return] paragraph 3 as follows:

  6. A return statement with an expression of type cv void can be used only in functions with a return type of cv void; the expression is evaluated just before the function returns to its caller.



1180. Over-aligned class types

Section: 3.11  [basic.align]     Status: review     Submitter: Clark Nelson     Date: 2010-08-25

Now that alignment can be applied directly to class types, the current wording of the note at the end of 3.11 [basic.align] paragraph 3 is no longer correct:

[Note: every over-aligned type is or contains a class type with a non-static data member to which an extended alignment has been applied. —end note]

Proposed resolution (November, 2010):

Change 3.11 [basic.align] paragraph 3 as follows:

[Note: every over-aligned type is or contains a class type with a non-static data member to which an extended alignment has been applied to which extended alignment applies (possibly through a non-static data member). —end note]



240. Uninitialized values and undefined behavior

Section: 4.1  [conv.lval]     Status: review     Submitter: Mike Miller     Date: 8 Aug 2000

4.1 [conv.lval] paragraph 1 says,

If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

I think there are at least three related issues around this specification:

  1. Presumably assigning a valid value to an uninitialized object allows it to participate in the lvalue-to-rvalue conversion without undefined behavior (otherwise the number of programs with defined behavior would be vanishingly small :-). However, the wording here just says "uninitialized" and doesn't mention assignment.

  2. There's no exception made for unsigned char types. The wording in 3.9.1 [basic.fundamental] was carefully crafted to allow use of unsigned char to access uninitialized data so that memcpy and such could be written in C++ without undefined behavior, but this statement undermines that intent.

  3. It's possible to get an uninitialized rvalue without invoking the lvalue-to-rvalue conversion. For instance:

            struct A {
                int i;
                A() { } // no init of A::i
            };
            int j = A().i;  // uninitialized rvalue
    

    There doesn't appear to be anything in the current IS wording that says that this is undefined behavior. My guess is that we thought that in placing the restriction on use of uninitialized objects in the lvalue-to-rvalue conversion we were catching all possible cases, but we missed this one.

In light of the above, I think the discussion of uninitialized objects ought to be removed from 4.1 [conv.lval] paragraph 1. Instead, something like the following ought to be added to 3.9 [basic.types] paragraph 4 (which is where the concept of "value" is introduced):

Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of any type other than char or unsigned char results in undefined behavior.

John Max Skaller:

A().i had better be an lvalue; the rules are wrong. Accessing a member of a structure requires it be converted to an lvalue, the above calculation is 'as if':

    struct A {
        int i;
        A *get() { return this; }
    };
    int j = (*A().get()).i;

and you can see the bracketed expression is an lvalue.

A consequence is:

    int &j= A().i; // OK, even if the temporary evaporates

j now refers to a 'destroyed' value. Any use of j is an error. But the binding at the time is valid.

Proposed Resolution (November, 2006):

  1. Add the indicated words to 3.9 [basic.types] paragraph 4:

    ... For trivial types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of a type other than unsigned char results in undefined behavior.
  2. Change 4.1 [conv.lval] paragraph 1 as follows:

    If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

Additional note (May, 2008):

The C committee is dealing with a similar issue in their DR336. According to this analysis, they plan to take almost the opposite approach to the one described above by augmenting the description of their version of the lvalue-to-rvalue conversion. The CWG did not consider that access to an unsigned char might still trap if it is allocated in a register and needs to reevaluate the proposed resolution in that light. See also issue 129.




342. Terminology: "indirection" versus "dereference"

Section: 5.3  [expr.unary]     Status: review     Submitter: Jason Merrill     Date: 7 Oct 2001

Split off from issue 315.

Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one.

Proposed resolution (December, 2006):

  1. Change 5.3.1 [expr.unary.op] paragraph 1 as follows:

  2. The unary * operator performs indirection dereferences a pointer value: the expression to which it is applied shall be a pointer...
  3. Change 8.3.4 [dcl.array] paragraph 8 as follows:

  4. The results are added and indirection applied values are added and the result is dereferenced to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers.
  5. Change 8.3.5 [dcl.fct] paragraph 9 as follows:

  6. The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through dereferencing the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through dereferencing a pointer to a function yields a function, which is then called.
  7. Change the index for * and “dereferencing” no longer to refer to “indirection.”

[Drafting note: 26.6.9 [template.indirect.array] requires no change. Many more places in the current wording use “dereferencing” than “indirection.”]




292. Deallocation on exception in new before arguments evaluated

Section: 5.3.4  [expr.new]     Status: review     Submitter: Andrei Iltchenko     Date: 26 Jun 2001

According to the C++ Standard section 5.3.4 [expr.new] paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.

On top of that paragraph 17 of the same section insists that

If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...

Now suppose we have:

  1. An implementation that always evaluates the constructor arguments first (for a new-expression that creates an object of a class type and has a new-initializer) and calls the allocation function afterwards.
  2. A class like this:
        struct  copy_throw  {
           copy_throw(const copy_throw&)
           {   throw  std::logic_error("Cannot copy!");   }
           copy_throw(long, copy_throw)
           {   }
           copy_throw()
           {   }
        };
    
  3. And a piece of code that looks like the one below:
        int  main()
        try  {
           copy_throw   an_object,     /* undefined behaviour */
              * a_pointer = ::new copy_throw(0, an_object);
           return  0;
        }
        catch(const std::logic_error&)
        {   }
    

Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 5.3.4 [expr.new] paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.

So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 5.3.4 [expr.new] paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4.

Suggested resolution:

Change the first sentence of 5.3.4 [expr.new] paragraph 17 to read:

If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...

Proposed resolution (March, 2008):

Change 5.3.4 [expr.new] paragraph 18 as follows:

If any part of the object initialization described above [Footnote: ...] terminates by throwing an exception, storage has been obtained for the object, and a suitable deallocation function can be found, the deallocation function is called...



236. Explicit temporaries and integral constant expressions

Section: 5.19  [expr.const]     Status: review     Submitter: Mike Miller     Date: 19 Jul 2000

Does an explicit temporary of an integral type qualify as an integral constant expression? For instance,

    void* p = int();    // well-formed?

It would appear to be, since int() is an explicit type conversion according to 5.2.3 [expr.type.conv] (at least, it's described in a section entitled "Explicit type conversion") and type conversions to integral types are permitted in integral constant expressions (5.19 [expr.const]). However, this reasoning is somewhat tenuous, and some at least have argued otherwise.

Note (March, 2008):

This issue should be closed as NAD as a result of the rewrite of 5.19 [expr.const] in conjunction with the constexpr proposal.




1098. Pointer conversions in constant expressions

Section: 5.19  [expr.const]     Status: review     Submitter: Jason Merrill     Date: 2010-08-01

One of the bullets in 5.19 [expr.const] paragraph 2 says,

This appears to prohibit conversion from one pointer type to another; for example,

    int x;
    constexpr void* p = &x;   // ill-formed

This seems excessive and probably unintentional.

Proposed resolution (November, 2010):

Change 5.19 [expr.const] paragraph 2 as follows:

[Note: the proposed resolution of issue 1188 edits this bullet in an incompatible fashion.]




1188. Type punning in constant expressions

Section: 5.19  [expr.const]     Status: review     Submitter: Jason Merrill     Date: 2010-08-31

The status of the following example is not clear:

    union U { float f; unsigned long u; };

    constexpr U u1 = { 1.0 };
    constexpr unsigned long u2 = u1.u;

This might be ill-formed because the aliasing causes undefined behavior, which should make the expression not a constant expression. However, a similar example using a permitted aliasing would presumably be acceptable:

    union U {
        unsigned char c[sizeof(double)];
        double d;
    };
    constexpr U c1u = { 0x12, 0x34 /* etc. */ };
    constexpr double c1 = c1u.d;

One suggestion was that unions should not be considered literal types, but some in the ensuing discussion deemed that excessive. That also would not address similar examples using reinterpret_cast, which is currently also permitted in constant expressions.

Proposed resolution (November, 2010):

Change 5.19 [expr.const] paragraph 2 as follows:

[Note: the proposed resolution of issue 1098 edits this bullet in an incompatible fashion.]




1204. Specifiers in a for-range-declaration

Section: 6.5  [stmt.iter]     Status: review     Submitter: Jason Merrill     Date: 2010-10-01

It seems unfortunate that the beginning of a C-style for loop can look like

whereas the beginning of a range-based for loop looks like

So that we don't know what constraints we are trying to apply to the specifiers until we see, or don't see, a :. The inconsistency between decl-specifier-seq and type-specifier-seq seems gratuitous and inconvenient.

Proposed resolution (November, 2010):

  1. Change the grammar 6.5 [stmt.iter] paragraph 1 as follows:

  2. Add the following as a new paragraph at the end of 6.5.4 [stmt.ranged]:

  3. The the decl-specifier-seq of a for-range-declaration, each decl-specifier shall be either a type-specifier or constexpr.



1018. Ambiguity between simple-declaration and attribute-declaration

Section: 7  [dcl.dcl]     Status: review     Submitter: Mike Miller     Date: 2010-01-04

The grammar for declarations includes the following two nonterminals:

An attribute-specifier followed by a semicolon could thus be parsed as either an attribute-declaration or as a simple-declaration that omits the optional decl-specifier-seq and init-declarator-list, and the current wording does not disambiguate the two. (There doesn't seem to be a compelling need for attribute-declaration as a separate nonterminal, given that simple-declaration can accommodate that form.)

Proposed resolution (February, 2011):

Change 7 [dcl.dcl] paragraph 1 as follows:

...The optional attribute-specifier-seq in a simple-declaration appertains to each of the entities declared by the declarators; it shall not appear if the optional of the init-declarator-list is omitted...



1042. Attributes in alias-declarations

Section: 7  [dcl.dcl]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-03-04

The grammar for an alias-declaration does not have a place for an attribute-specifier, although a typedef declaration does. Since an alias-declaration is essentially a different syntactic form of a typedef declaration (7.1.3 [dcl.typedef] paragraph 2), this could be surprising.

Proposed resolution (February, 2011):

  1. Change the grammar in 7 [dcl.dcl] paragraph 1 as follows:

  2. Change 7.1.3 [dcl.typedef] paragraph 2 as follows:

  3. A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics...



1186. Non-dependent constexpr violations in function templates

Section: 7.1.5  [dcl.constexpr]     Status: review     Submitter: Jason Merrill     Date: 2010-08-30

7.1.5 [dcl.constexpr] paragraph 5 says,

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function is rendered not constexpr by a non-dependent construct. —end note]

A non-dependent error in a function template renders it ill-formed with no diagnostic required (14.6 [temp.res] paragraph 8). A similar approach is being taken in the proposed resolution of issue 1125 with respect to constexpr functions. It would be more consistent to treat constexpr function templates in the same way, along the lines of

If no specialization of the template would be constexpr, the program is ill-formed, no diagnostic required.

Proposed resolution (November, 2010):

Change 7.1.5 [dcl.constexpr] paragraph 6 as follows:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function is rendered not constexpr by a non-dependent construct.end note] If no specialization of the template would yield a constexpr function or constexpr constructor, the program is ill-formed; no diagnostic required.



1194. Constexpr references

Section: 7.1.5  [dcl.constexpr]     Status: review     Submitter: Daniel Krügler     Date: 2010-09-04

7.1.5 [dcl.constexpr] restricts the constexpr specifier to object and function declarations. Especially given the support for reference types in constexpr functions, and considering that constexpr pointer declarations are permitted, there does not seem to be a good reason for excluding constexpr references.

(See also issue 1195.)

Proposed resolution (November. 2010):

  1. Change 7.1.5 [dcl.constexpr] paragraph 1 as follows:

  2. The constexpr specifier shall be applied only to the definition of an object a variable, the declaration of a function...
  3. Change 7.1.5 [dcl.constexpr] paragraph 8 as follows:

  4. A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized. If it is initialized by a constructor call, the constructor shall be a constexpr constructor and every argument to the constructor shall be a constant expression. Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression...



1195. References to non-literal types in constexpr functions

Section: 7.1.5  [dcl.constexpr]     Status: review     Submitter: Daniel Krügler     Date: 2010-09-05

7.1.5 [dcl.constexpr] paragraph 3 is overly restrictive in requiring that reference parameter and return types of a constexpr function or constructor must refer to a literal type. 5.19 [expr.const] paragraph 2 already prevents any problematic uses of lvalues of non-literal types, and it permits use of pointers to non-literal types as address constants. The same should be permitted via reference parameters and return types of constexpr functions.

(See also issue 1194.)

Proposed resolution (November, 2010):

  1. Change 3.9 [basic.types] paragraph 10 as follows:

  2. A type is a literal type if it is:

  3. Change 7.1.5 [dcl.constexpr] paragraph 3 as follows:

  4. The definition of a constexpr function shall satisfy the following constraints:

  5. Change 7.1.5 [dcl.constexpr] paragraph 4 as follows:

  6. The definition of a constexpr constructor shall satisfy the following constraints:




1225. constexpr constructors and virtual bases

Section: 7.1.5  [dcl.constexpr]     Status: review     Submitter: Jason Merrill     Date: 2010-10-26

A class with a virtual base should not be allowed to have a constexpr constructor.

Proposed resolution (November, 2010):

Add the following bullet to the list in 7.1.5 [dcl.constexpr] paragraph 4:




539. Constraints on type-specifier-seq

Section: 7.1.6  [dcl.type]     Status: review     Submitter: Mike Miller     Date: 5 October 2005

The constraints on type-specifiers given in 7.1.6 [dcl.type] paragraphs 2 and 3 (at most one type-specifier except as specified, at least one type-specifier, no redundant cv-qualifiers) are couched in terms of decl-specifier-seqs and declarations. However, they should also apply to constructs that are not syntactically declarations and that are defined to use type-specifier-seqs, including 5.3.4 [expr.new], 6.6 [stmt.jump], 8.1 [dcl.name], and 12.3.2 [class.conv.fct].

Proposed resolution (March, 2008):

Change 7.1.6 [dcl.type] paragraph 3 as follows:

At In a complete type-specifier-seq or in a complete decl-specifier-seq of a declaration, at least one type-specifier that is not a cv-qualifier is required in a declaration shall appear unless it the declaration declares a constructor, destructor or conversion function.

(Note: paper N2546, voted into the Working Draft in February, 2008, addresses part of this issue.)




1212. Non-function-call xvalues and decltype

Section: 7.1.6.2  [dcl.type.simple]     Status: review     Submitter: Daniel Krügler     Date: 2010-10-21

Given

    int&& f();
    int i;

it is surprising that decltype(f()) and decltype(static_cast<int&&>(i)) are not the same type.

Proposed resolution (November, 2010):

Change 7.1.6.2 [dcl.type.simple] paragraph 4 as follows:

The type denoted by decltype(e) is defined as follows:




1185. Misleading description of language linkage and member function types

Section: 7.5  [dcl.link]     Status: review     Submitter: Mike Miller     Date: 2010-08-29

The current wording of 7.5 [dcl.link] paragraph 4 is:

...A C language linkage is ignored for the names of class members and the member function type of class member functions...

This has two problems. First, it sounds as if a class member function has a “member function type,” while in fact the type of a class member function is an ordinary function type (cf 9.2 [class.mem] paragraph 11).

Second, even if that problem is fixed, it is not accurate to say that a C language linkage is “ignored” for the type of a member function. It does not affect the language linkage of the type of the member function, but it does affect the language linkage of any function declarators appearing in the parameter and return types of the function and thus the type of the function.

Proposed resolution (November, 2010):

Change 7.5 [dcl.link] paragraph 4 as follows:

...A C language linkage is ignored for in determining the language linkage of the names of class members and the member function type of class member functions...



1031. Optional elements in attributes

Section: 7.6.1  [dcl.attr.grammar]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-02-10

An attribute-argument-clause should be allowed to consist solely of (), i.e., with no balanced-token-seq. Furthermore, the grammar for balanced-token should make the balanced-token-seq optional. Both of these goals could be accomplished by making the balanced-token optional in the first production of the rule for balanced-token-seq.

Proposed resolution (February, 2011):

Change the grammar of 7.6.1 [dcl.attr.grammar] paragraph 1 as follows:




1033. Restrictions on alignment attributes

Section: 7.6.2  [dcl.align]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-02-17

According to 7.6.2 [dcl.align] paragraph 5,

The combined effect of all alignment attributes in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the entity being declared.

“...would otherwise be required” could be read as referring to the alignment set by another declaration of the entity. However, it was intended to prevent specifying an alignment smaller than the natural alignment the entity would have in the absence of an align attribute. The wording should be changed to make that clearer.

Proposed resolution (February, 2011):

Change 7.6.2 [dcl.align] paragraph 5 as follows:

The combined effect of all alignment-specifiers in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the entity being declared if all alignment-specifiers were ignored (including those in other declarations).



482. Qualified declarators in redeclarations

Section: 8.3  [dcl.meaning]     Status: review     Submitter: Daveed Vandevoorde     Date: 03 Nov 2004

According to 8.3 [dcl.meaning] paragraph 1,

A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers...

This restriction prohibits examples like the following:

    void f();
    void ::f();        // error: qualified declarator

    namespace N {
      void f();
      void N::f() { }  // error: qualified declarator
    }

There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?

Notes from the April, 2006 meeting:

In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.

Proposed resolution (October, 2006):

Remove the indicated words from 8.3 [dcl.meaning] paragraph 1:

...An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 [class.conv], 12.4 [class.dtor], 13.5 [over.oper]) and for the declaration of template specializations or partial specializations (). A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id...

[Drafting note: The omission of “outside of its class” here does not give permission for redeclaration of class members; that is still prohibited by 9.2 [class.mem] paragraph 1. The removal of the enumeration of the kinds of declarations in which a qualified-id can appear does allow a typedef declaration to use a qualified-id, which was not permitted before; if that is undesirable, the prohibition can be reinstated here.]




547. Partial specialization on member function types

Section: 8.3.5  [dcl.fct]     Status: review     Submitter: Peter Dimov     Date: 04 November 2005

The following example appears to be well-formed, with the partial specialization matching the type of Y::f(), even though it is rejected by many compilers:

    template<class T> struct X;

    template<class R> struct X< R() > {
    };

    template<class F, class T> void test(F T::* pmf) {
        X<F> x;
    }

    struct Y {
        void f() {
        }
    };

    int main() {
        test( &Y::f );
    }

However, 8.3.5 [dcl.fct] paragraph 4 says,

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored.

This specification makes it impossible to write a partial specialization for a const member function:

    template<class R> struct X<R() const> {
    };

A template argument is not one of the permitted contexts for cv-qualification of a function type. This restriction should be removed.

Notes from the April, 2006 meeting:

During the meeting the CWG was of the opinion that the “R() const” specialization would not match the const member function even if it were allowed and so classified the issue as NAD. Questions have been raised since the meeting, however, suggesting that the template argument in the partial specialization would, in fact, match the type of a const member function (see, for example, the very similar usage via typedefs in 9.3 [class.mfct] paragraph 9). The issue is thus being left open for renewed discussion at the next meeting.

Proposed resolution (June, 2008):

Change 8.3.5 [dcl.fct] paragraph 7 as follows:

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The effect... A ref-qualifier shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The return type...



1183. Expansion of parameter packs in declarators

Section: 8.3.5  [dcl.fct]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-08-26

8.3.5 [dcl.fct] paragraph 13 says,

The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack.

I think that's incorrect. For example, I think

    template<class... P> void f(int (* ...p)[sizeof...(P)]);

should be an error, and that the function parameter pack p does not expand the template parameter pack P in this case.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 778.




1058. Reference binding of incompatible array types

Section: 8.5.3  [dcl.init.ref]     Status: review     Submitter: Johannes Schaub     Date: 2010-03-20

According to the logic in 8.5.3 [dcl.init.ref] paragraph 5, the following example should create a temporary array and bind the reference to that temporary:

    const char (&p)[10] = "123";

That is presumably not intended (issue 450 calls a similar outcome for rvalue arrays “implausible”). Current implementations reject this example.

Rationale (August, 2010):

The Standard does not describe initialization of array temporaries, so a program that requires such is ill-formed.

Note (October, 2010):

Although in general an object of array type cannot be initialized from another object of array type, there is special provision in 8.5.2 [dcl.init.string] for doing so when the source object is a string literal, as in this example. The issue is thus being reopened for further consideration in this light.

Notes from the November, 2010 meeting:

The CWG agreed that the current wording appears to permit this example but still felt that array temporaries are undesirable. Wording should be added to disallow this usage.

Proposed resolution (November, 2010):

Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:

(See also issue 1232, which argues in favor of allowing array temporaries.)




1095. List-initialization of references

Section: 8.5.4  [dcl.init.list]     Status: review     Submitter: Daniel Krügler     Date: 2010-07-26

The current wording of the WP appears not to allow for list-initialization of a reference like the following:

    int i;
    int& ir{i};

First, 8.5 [dcl.init] paragraph 16 bullet 1 reads,

A reference is not an object, so this does not appear to apply; however, the second bullet sends reference initialization off to 8.5.3 [dcl.init.ref], which does not cover braced-init-lists: paragraph 5 of that section deals only with initilizer expressions, and a braced-init-list is not an expression.

Assuming that the use of “object” in the first bullet is just an oversight, 8.5.4 [dcl.init.list] also does not cover the case of a reference to a scalar type whose initalizer is a braced-init-list with a single element. Bullet 7 of paragraph 3 reads,

and would cover this case except that, again, a reference is not an object. As a result, such an initialization would end up in the last bullet and consequently be ill-formed.

Presumably all that is needed is to add “or reference” to the appropriate bullets of 8.5 [dcl.init] paragraph 16 and 8.5.4 [dcl.init.list] paragraph 3.

Proposed resolution (November, 2010):

  1. Change 8.5 [dcl.init] paragraph 16 bullet 1 as follows:

  2. Change 8.5.4 [dcl.init.list] paragraph 3 bullet 7 as follows:




1215. Definition of POD struct

Section: 9  [class]     Status: review     Submitter: Daniel Krügler     Date: 2010-10-28

The definition of a POD struct in 9 [class] paragraph 9 is not (but should be) restricted to non-union class types.

Proposed resolution (November, 2010):

Change 9 [class] paragraph 9 as follows:

A POD struct109 is a non-union class that is both a trivial class and a standard-layout class...



1035. Omitted and required decl-specifiers

Section: 9.2  [class.mem]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-02-25

According to 9.2 [class.mem] paragraph 6,

The decl-specifier-seq is omitted in constructor, destructor, and conversion function declarations only.

This is incorrect, as some decl-specifiers (explicit, virtual, inline, constexpr) are permitted in these declarations. Conversely, C.1.5 [diff.dcl], “Banning implicit int,” says,

In C++ a decl-specifier-seq must contain a type-specifier.

This is also incorrect for these declarations.

Proposed resolution (February, 2011):

  1. Change 9.2 [class.mem] paragraph 7 as follows:

  2. The decl-specifier-seq is may be omitted in constructor, destructor, and conversion function declarations only; when declaring another kind of member the decl-specifier-seq shall contain a type-specifier that is not a cv-qualifier. The member-declarator-list can be omitted...
  3. Change C.1.5 [diff.dcl], “Banning implicit int,” as follows:

  4. In C++ a decl-specifier-seq must contain a type-specifier, unless it is followed by a declarator for a constructor, a destructor, or a conversion function. In the following example...



1101. Non-integral initialized static data members

Section: 9.4.2  [class.static.data]     Status: review     Submitter: Jason Merrill     Date: 2010-08-02

According to 9.4.2 [class.static.data] paragraph 3,

If a static data member is of const literal type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [Note: In both these cases, the member may appear in constant expressions. —end note]

However, 5.19 [expr.const] paragraph 2 bullet 7 allows only integral and enumeration types in constant expressions for the const case; the other types require constexpr to be considered constant expressions.

Proposed resolution (November, 2010):

Change 9.4.2 [class.static.data] paragraph 3 as follows:

If a non-volatile const static data member is of const literal integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.19 [expr.const]). A static data member of literal type can be declared in the class definition with the constexpr specifier...



512. Union members with user-declared non-default constructors

Section: 9.5  [class.union]     Status: review     Submitter: Alisdair Meredith     Date: 19 Mar 2005

Can a member of a union be of a class that has a user-declared non-default constructor? The restrictions on union membership in 9.5 [class.union] paragraph 1 only mention default and copy constructors:

An object of a class with a non-trivial default constructor (12.1 [class.ctor]), a non-trivial copy constructor (12.8 [class.copy]), a non-trivial destructor (12.4 [class.dtor]), or a non-trivial copy assignment operator (13.5.3 [over.ass], 12.8 [class.copy]) cannot be a member of a union...

(12.1 [class.ctor] paragraph 11 does say, “a non-trivial constructor,” but it's not clear whether that was intended to refer only to default and copy constructors or to any user-declared constructor. For example, 12.2 [class.temporary] paragraph 3 also speaks of a “non-trivial constructor,” but the cross-references there make it clear that only default and copy constructors are in view.)

Note (March, 2008):

This issue was resolved by the adoption of paper J16/08-0054 = WG21 N2544 (“Unrestricted Unions”) at the Bellevue meeting.




462. Lifetime of temporaries bound to comma expressions

Section: 12.2  [class.temporary]     Status: review     Submitter: Steve Adamczyk     Date: April 2004

Split off from issue 86.

Should binding a reference to the result of a "," operation whose second operand is a temporary extend the lifetime of the temporary?

  const SFileName &C = ( f(), SFileName("abc") );

Notes from the March 2004 meeting:

We think the temporary should be extended.

Proposed resolution (October, 2004):

Change 12.2 [class.temporary] paragraph 2 as indicated:

... In all these cases, the temporaries created during the evaluation of the expression initializing the reference, except the temporary that is the overall result of the expression [Footnote: For example, if the expression is a comma expression (5.18 [expr.comma]) and the value of its second operand is a temporary, the reference is bound to that temporary.] and to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction...

[Note: this wording partially resolves issue 86. See also issue 446.]

Notes from the April, 2005 meeting:

The CWG suggested a different approach from the 10/2004 resolution, leaving 12.2 [class.temporary] unchanged and adding normative wording to 5.18 [expr.comma] specifying that, if the result of the second operand is a temporary, that temporary is the result of the comma expression as well.

Proposed Resolution (November, 2006):

Add the indicated wording to 5.18 [expr.comma] paragraph 1:

... The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is an lvalue, and is a bit-field if its right operand is an lvalue and a bit-field. If the value of the right operand is a temporary (12.2 [class.temporary]), the result is that temporary.



395. Conversion operator template syntax

Section: 12.3.2  [class.conv.fct]     Status: review     Submitter: Daveed Vandevoorde     Date: 18 Dec 2002

A posting in comp.lang.c++.moderated prompted me to try the following code:

  struct S {
    template<typename T, int N> (&operator T())[N];
  };

The goal is to have a (deducible) conversion operator template to a reference-to-array type.

This is accepted by several front ends (g++, EDG), but I now believe that 12.3.2 [class.conv.fct] paragraph 1 actually prohibits this. The issue here is that we do in fact specify (part of) a return type.

OTOH, I think it is legitimate to expect that this is expressible in the language (preferably not using the syntax above ;-). Maybe we should extend the syntax to allow the following alternative?

  struct S {
    template<typename T, int N> operator (T(&)[N])();
  };

Eric Niebler: If the syntax is extended to support this, similar constructs should also be considered. For instance, I can't for the life of me figure out how to write a conversion member function template to return a member function pointer. It could be useful if you were defining a null_t type. This is probably due to my own ignorance, but getting the syntax right is tricky.

Eg.

  struct null_t {
    // null object pointer. works.
    template<typename T> operator T*() const { return 0; }
    // null member pointer. works.
    template<typename T,typename U> operator T U::*() const { return 0; }
    // null member fn ptr.  doesn't work (with Comeau online).  my error?
    template<typename T,typename U> operator T (U::*)()() const { return 0; }
  };

Martin Sebor: Intriguing question. I have no idea how to do it in a single declaration but splitting it up into two steps seems to work:

  struct null_t {
    template <class T, class U>
    struct ptr_mem_fun_t {
      typedef T (U::*type)();
    };

    template <class T, class U>
    operator typename ptr_mem_fun_t<T, U>::type () const {
      return 0;
    }
  };

Note: In the April 2003 meeting, the core working group noticed that the above doesn't actually work.

Note (June, 2010):

It has been suggested that template aliases effectively address this issue. In particular, an identity alias like

    template<typename T> using id = T;

provides the necessary syntactic sugar to be able to specify types with trailing declarator elements as a conversion-type-id. For example, the two cases discussed above could be written as:

    struct S {
        template<typename T, int N>
          operator id<T[N]>&();
        template<typename T, typename U>
          operator id<T (U::*)()>() const;
    };

This issue should thus be closed as now NAD.




111. Copy constructors and cv-qualifiers

Section: 12.8  [class.copy]     Status: review     Submitter: Jack Rouse     Date: 4 May 1999

Jack Rouse: In 12.8 [class.copy] paragraph 8, the standard includes the following about the copying of class subobjects in such a constructor:

But there can be multiple copy constructors declared by the user with differing cv-qualifiers on the source parameter. I would assume overload resolution would be used in such cases. If so then the passage above seems insufficient.

Mike Miller: I'm more concerned about 12.8 [class.copy] paragraph 7, which lists the situations in which an implicitly-defined copy constructor can render a program ill-formed. Inaccessible and ambiguous copy constructors are listed, but not a copy constructor with a cv-qualification mismatch. These two paragraphs taken together could be read as requiring the calling of a copy constructor with a non-const reference parameter for a const data member.

Proposed Resolution (November, 2006):

This issue is resolved by the proposed resolution for issue 535.




535. Copy construction without a copy constructor

Section: 12.8  [class.copy]     Status: review     Submitter: Mike Miller     Date: 7 October 2005

Footnote 112 (12.8 [class.copy] paragraph 2) says,

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

However, many of the stipulations about copy construction are phrased to refer only to “copy constructors.” For example, 12.8 [class.copy] paragraph 14 says,

A program is ill-formed if the copy constructor... for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]).

Does that mean that using an inaccessible template constructor to copy an object is permissible, because it is not a “copy constructor?” Obviously not, but each use of the term “copy constructor” in the Standard should be examined to determine if it applies strictly to copy constructors or to any constructor used for copying. (A similar issue applies to “copy assignment operators,” which have the same relationship to assignment operator function templates.)

Proposed Resolution (February, 2008):

  1. Change 3.2 [basic.def.odr] paragraph 2 as follows:

  2. ... [Note: this covers calls to named functions (5.2.2 [expr.call]), operator overloading (clause 13 [over]), user-defined conversions (12.3.2 [class.conv.fct]), allocation function for placement new (5.3.4 [expr.new]), as well as non-default initialization (8.5 [dcl.init]). A copy constructor selected to copy class objects is used even if the call is actually elided by the implementation (12.8 [class.copy]). —end note] ... A copy-assignment function for a class An assignment operator function in a class is used by an implicitly-defined copy-assignment function for another class as specified in 12.8 [class.copy]...
  3. Delete 12.1 [class.ctor] paragraphs 10 and 11:

  4. A copy constructor (12.8 [class.copy]) is used to copy objects of class type.

    A union member shall not be of a class type (or array thereof) that has a non-trivial constructor.

  5. Replace the “example” in 12.2 [class.temporary] paragraph 1 with a note as follows:

  6. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11 [class.access]), shall be satisfied. —end example] [Note: This includes accessibility (clause 11 [class.access]) for the constructor selected. —end note]
  7. Change 12.8 [class.copy] paragraph 7 as follows:

  8. A non-user-provided copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type (3.2 [basic.def.odr]). [Footnote: See 8.5 [dcl.init] for more details on direct and copy initialization. —end footnote] [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2 [class.temporary]) the copy operation (12.8 [class.copy]). —end note] A program is ill-formed if the class for which a copy constructor is implicitly defined or explicitly defaulted has:

    • a non-static data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or

    • a base class with an inaccessible or ambiguous copy constructor.

    Before the non-user-provided copy constructor for a class is implicitly defined...

  9. Change 12.8 [class.copy] paragraph 8 as follows:

  10. ...Each subobject is copied in the manner appropriate to its type:

    [Drafting note: 8.5 [dcl.init] paragraph 15 requires “unambiguous” and 13.3 [over.match] paragraph 3 requires “accessible,” thus no need for normative text here.]

  11. Change 12.8 [class.copy] paragraph 12 as follows:

  12. A non-user-provided copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type it is used (3.2 [basic.def.odr]). A program is ill-formed if the class for which a copy assignment operator is implicitly defined or explicitly defaulted has: a non-static data member of const or reference type.

    • a non-static data member of const type, or

    • a non-static data member of reference type, or

    • a non-static data member of class type (or array thereof) with an inaccessible copy assignment operator, or

    • a base class with an inaccessible copy assignment operator.

  13. Change 12.8 [class.copy] paragraph 13 as follows:

  14. ... Each subobject is assigned in the manner appropriate to its type:

  15. Delete 12.8 [class.copy] paragraph 14:

  16. A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]). [Note: Copying one object into another using the copy constructor or the copy assignment operator does not change the layout or size of either object. —end note]
  17. Change 12.8 [class.copy] paragraph 15 as follows:

  18. When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor selected for the copy operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. [Footnote: Because only one object is destroyed instead of two, and one copy constructor is not executed, there is still one object destroyed for each one constructed. —end footnote] This elision...
  19. Change 13.3.3.1.2 [over.ics.user] paragraph 4 as follows:

  20. A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy constructor (i.e., a user-defined conversion function) is called for those cases.
  21. Change 15.1 [except.throw] paragraph 3 as follows:

  22. A throw-expression initializes a temporary object, called the exception object, the type of which by copy-initialization (8.5 [dcl.init]). The type of that temporary object is determined...
  23. Change 15.1 [except.throw] paragraph 5 as follows:

  24. When the thrown object is a class object, the copy constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy operation is elided (12.8 [class.copy]).
  25. Change 15.3 [except.handle] paragraphs 16-17 as follows:

  26. When the exception-declaration specifies a class type, a copy constructor copy-initialization (8.5 [dcl.init]) is used to initialize either the object declared in the exception-declaration or, if the exception-declaration does not specify a name, a temporary object of that type. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler. The copy constructor selected for the copy-initialization and the destructor shall be accessible in the context of the handler, even if the copy operation is elided (12.8 [class.copy]). If the copy constructor and destructor are implicitly declared (12.8 [class.copy]), such a use in the handler causes these functions to be implicitly defined; otherwise, the program shall provide a definition for these functions.

    The copy constructor and destructor associated with the object shall be accessible even if the copy operation is elided (12.8 [class.copy]).

  27. Change the footnote in 15.5.1 [except.terminate] paragraph 1 as follows:

  28. [Footnote: For example, if the object being thrown is of a class with a copy constructor type, std::terminate() will be called if that copy constructor the constructor selected to copy the object exits with an exception during a throw. —end footnote]

(This resolution also resolves issue 111.)

[Drafting note: The following do not require changes: 5.17 [expr.ass] paragraph 4; 9 [class] paragraph 5; 9.5 [class.union] paragraph 1; 12.2 [class.temporary] paragraph 2; 12.8 [class.copy] paragraphs 1-2; 15.4 [except.spec] paragraph 14.]

Notes from February, 2008 meeting:

These changes overlap those that will be made when concepts are added. This issue will be maintained in “review” status until the concepts proposal is adopted and any conflicts will be resolved at that point.




1224. constexpr defaulted copy constructors

Section: 12.8  [class.copy]     Status: review     Submitter: Jason Merrill     Date: 2010-10-25

12.1 [class.ctor] allows for a defaulted default constructor to be constexpr, but 12.8 [class.copy] does not do the same for a defaulted copy constructor. This seems wrong.

Proposed resolution (November, 2010):

Change 12.8 [class.copy] paragraph 14 as follows:

A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (3.2 [basic.def.odr]) to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type123 or when it is explicitly defaulted after its first declaration. [Note: the copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2 [basic.def.odr], 12.2 [class.temporary]). —end note] If the implicitly-defined constructor would satisfy the requirements of a constexpr constructor (7.1.5 [dcl.constexpr]), the implicitly-defined constructor is constexpr.



1079. Overload resolution involving aggregate initialization

Section: 13.3.3.2  [over.ics.rank]     Status: review     Submitter: Jason Merrill     Date: 2010-06-15

The current wording makes some calls involving aggregate initialization ambiguous that should not be. For example, the calls below to f and g should each prefer the second overload:

    struct A { int i; };

    void f (const A &);
    void f (A &&);

    void g (A, double);
    void g (A, int);

    int main() {
       f ( { 1 } );
       g ( { 1 }, 1 );
    }

Proposed resolution (August, 2010):

Change 13.3.3.2 [over.ics.rank] paragraph 3 bullet 2 as follows:




778. Template parameter packs in non-type template parameters

Section: 14.1  [temp.param]     Status: review     Submitter: Michael Wong     Date: 13 February, 2009

Consider an example like:

    template <typename T, T Value> struct bar { };
    template <typename... T, T ...Value> void foo(bar<T, Value>);

The current wording in 14.1 [temp.param] is unclear as to whether this is permitted or not. For comparison, 8.3.5 [dcl.fct] paragraph 13 says,

A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack (14.5.3 [temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack (14.5.3 [temp.variadic]). [Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see 14.1 [temp.param]. —end note] A function parameter pack, if present, shall occur at the end of the parameter-declaration-list. The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack.

The requirement here that the type of a function parameter pack must contain a template parameter pack is not repeated for template non-type parameters in 14.1 [temp.param], nor is the statement that it expands the template parameter pack.

A related issue is that neither function nor template parameter packs are listed in 14.5.3 [temp.variadic] paragraph 4 among the contexts in which a pack expansion can appear.

Proposed resolution (November, 2010):

  1. Change 5.3.3 [expr.sizeof] paragraph 5 as follows:

  2. The identifier in a sizeof... expression shall name a parameter pack. The sizeof... operator yields the number of arguments provided for the parameter pack identifier. The parameter pack is expanded (14.5.3 [temp.variadic]) by the sizeof... operator A sizeof... expression is a pack expansion (14.5.3 [temp.variadic]). [Example:...
  3. Change 8.3.5 [dcl.fct] paragraph 13 as follows:

  4. A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack (14.5.3 [temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack (14.5.3 [temp.variadic]). [Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see 14.1 [temp.param]. —end note] The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack A function parameter pack is a pack expansion (14.5.3 [temp.variadic]). [Example:...
  5. Change 14.1 [temp.param] paragraph 15 as follows:

  6. If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a parameter pack (8.3.5 [dcl.fct]), then the template-parameter is a template parameter pack (14.5.3 [temp.variadic]). A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded parameter packs is a pack expansion. Similarly, a template parameter pack that is a type-parameter with a template-parameter-list containing one or more unexpanded parameter packs is a pack expansion. [Example:

      template <class... Types> class Tuple; // Types is a template type parameter pack and a pack expansion
      template <class T, int... Dims> struct multi_array; // Dims is a non-type template parameter pack but not a pack expansion
      template <class T, T... Values> struct static_array; // Values is a non-type template parameter pack and a pack expansion
    
  7. Change 14.5.3 [temp.variadic] paragraphs 4-6 and add a new paragraph 7 as follows:

  8. A pack expansion is a sequence of tokens that names one or more parameter packs, followed by an ellipsis. The sequence of tokens is called the pattern of the expansion; its syntax consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:

    [Example:...

    A parameter pack whose name appears within the pattern of a pack expansion is expanded by that pack expansion. An appearance of the name of a parameter pack is only expanded by the innermost enclosing pack expansion. The pattern of a pack expansion shall name one or more parameter packs that are not expanded by a nested pack expansion; such parameter packs are called unexpanded parameter packs in the pattern. All of the parameter packs expanded...

      ...
      void g(Args ... args) {  // OK: “Args” is expanded by the function parameter pack “args
      ...
    

    The instantiation of an a pack expansion that is not a sizeof... expression produces a list...

    The instantiation of a sizeof... expression (5.3.3 [expr.sizeof]) produces an integral constant containing the number of elements in the parameter pack it expands.

This resolution also resolves issues 1182 and 1183.

Additional note (February, 2011):

A problematic case is a function like

  template<typename... T, T... t> void f(T...) { }

where each element of the nontype pack actually has a different type. This causes problems for template argument deduction, since T and t are supposed to be deduced independently, but they're linked through their sizes. There doesn't appear to be any use case for this kind of example, so it should be ill-formed.

The rule should probably be to consider a non-type template parameter pack that expands any template parameter packs from the same template-parameter-list as ill-formed.




1206. Defining opaque enumeration members of class templates

Section: 14.5.1  [temp.class]     Status: review     Submitter: Jason Merrill     Date: 2010-10-06

Presumably an out-of-class definition for an opaque enumeration member of a class template is intended to be allowed; however, the current wording of 14.5.1 [temp.class] provides only for out-of-class definitions of member functions, member classes, static data members, and member templates, not for opaque enumerations.

Proposed resolution (November, 2010):

  1. Change 14 [temp] paragraph 1 as follows:

  2. ...The declaration in a template-declaration shall

  3. Change 14.5.1 [temp.class] paragraph 3 as follows:

  4. When a member function, a member class, a member enumeration, a static data member or a member template of a class template is defined outside of the class template definition...
  5. Add a new section following 14.5.1.3 [temp.static]:

  6. 14.5.1.4 Enumeration members of class templates [temp.mem.enum]

    An enumeration member of a class template may be defined outside the class template definition. [Example:

      template<class T> struct A {
        enum E: T;
      };
      A<int> a;
      template<class T> enum A<T>::E: T { e1, e2 };
      A<int>::E e = A<int>::e1;
    

    end example]

  7. Change 14.7 [temp.spec] paragraph 2 as follows:

  8. A function instantiated from a function template is called an instantiated function. A class instantiated from a class template is called an instantiated class. A member function, a member class, a member enumeration, or a static data member of a class template instantiated from the member definition of the class template is called, respectively, an instantiated member function, member class, member enumeration, or static data member. A member function...
  9. Change 14.7.1 [temp.inst] paragraph 1 as follows:

  10. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, scoped member enumerations, static data members and member templates; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. Unless a member...
  11. Change 14.7.3 [temp.expl.spec] paragraph 1 as follows:

  12. An explicit specialization of any of the following:

  13. Change 14.7.3 [temp.expl.spec] paragraph 4 as follows:

  14. A member function, a member class, a member enumeration, or a static data member of a class template may be explicitly specialized for a class specialization that is implicitly instantiated...
  15. Add the indicated text to the example in 14.7 [temp.spec] paragraph 6:

  16.   template<> void sort<>(Array(<char*>& v);        // OK: sort<char*> not yet used
      template<class T> struct A {
        enum E: T;
        enum class S: T;
      };
      template<> enum A<int>::E: int { eint };         // OK
      template<> enum class A<int>::S: int { sint };   // OK
      template<class T> enum A<T>::E: T { eT };
      template<class T> enum class A<T>::S: T { sT };
      template<> enum A<char>::E: int { echar };       // ill-formed, A<char>::E was instantiated when A<char> was instantiated
      template<> enum class A<char>::S: int { schar }; // OK
    
  17. Change 14.7.3 [temp.expl.spec] paragraph 7 as follows:

  18. The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates...



1182. Incorrect description of pack expansion syntax

Section: 14.5.3  [temp.variadic]     Status: review     Submitter: Daveed Vandevoorde     Date: 2010-08-26

According to 14.5.3 [temp.variadic] paragraph 4,

A pack expansion is a sequence of tokens that names one or more parameter packs, followed by an ellipsis.

This is contradicted by 5.3.3 [expr.sizeof] paragraph 5, which describes sizeof...(Types) as an expansion, as well as the case where the expansion appears in a declarator like the example given in 8.3.5 [dcl.fct] paragraph 13:

    template<typename... T> void f(T (* ...t)(int, int));

This is also described as a pack expansion, although it does not fit the syntactic summary.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 778.




1056. Template aliases, member definitions, and the current instantiation

Section: 14.5.7  [temp.alias]     Status: review     Submitter: John Spicer     Date: 2010-03-17

According to 14.6.2.1 [temp.dep.type] paragraph 1, in a primary class template a name refers to the current instantiation if it is the injected-class-name or the name of the class template followed by the template argument list of the template. Although a template-id referring to a specialization of a template alias is described as “equivalent to” the associated type, a specialization of a template alias is neither of the things that qualifies as naming the current instantiation, so presumably the typename keyword in the following example is required:

    template <class T> struct A;
    template <class T> using B = A<T>;

    template <class T> struct A {
        struct C {};
        typename B<T>::C bc;  // typename needed
    };

(However, the list in 14.6.2.1 [temp.dep.type] may not be exactly what we want; it doesn't allow use of a typedef denoting the type of the current instantiation, either, but that presumably should be accepted.)

For analogous reasons, it should not be permitted to use a template alias as a nested-name-specifier when defining the members of a class template:

    template <class T> struct A {
        void g();
    };
    template <class T> using B = A<T>;
    template <class T> void B<T>::g() {} // error

Notes from the November, 2010 meeting:

The CWG disagreed with the suggested direction, feeling that aliases should work like typedefs and that the examples should be accepted.

Proposed resolution (November, 2010):

  1. Change 14.6.2.1 [temp.dep.type] paragraph 1 as follows:

  2. In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is

    Change 14.6.2.1 [temp.dep.type] paragraph 3 as follows:

    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, except that a decltype-specifier that denotes a dependent type is always considered non-equivalent. 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 in which the template parameter appears as a subexpression. [Example:...

This resolution also resolves issue 1057.




602. When is the injected-class-name of a class template a template?

Section: 14.6.1  [temp.local]     Status: review     Submitter: Daveed Vandevoorde     Date: 23 October 2006

Consider the following example:

    template<class T>
    struct A {
         template<class U>
             friend struct A; // Which A?
    };

Presumably the lookup for A in the friend declaration finds the injected-class-name of the template. However, according to 14.6.1 [temp.local] paragraph 1,

The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.

If that rule applies, then this example is ill-formed (because you can't have a template-argument-list in a class template declaration that is not a partial specialization).

Mike Miller: The injected-class-name has a dual nature, as described in 14.6.1 [temp.local], acting as either a template name or a class name, depending on the context; a template argument list forces the name to be interpreted as a template. It seems reasonable that in this example the injected-class-name has to be understood as referring to the class template; a template header is at least as strong a contextual indicator as a template argument list. However, the current wording doesn't say that.

(See also issue 1004.)

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1004.




1004. Injected-class-names as arguments for template template parameters

Section: 14.6.1  [temp.local]     Status: review     Submitter: Jason Merrill     Date: 2009-11-19

The injected-class-name of a class template can be used either by itself, in which case it is a type denoting the current instantiation, or followed by a template argument list, in which case it is a template-name. It would be helpful to extend this treatment so that the injected-class-name could be used as an argument for a template template parameter:

    template <class T> struct A { };

    template <template <class> class TTP> struct B { };

    struct C: A<int> {
       B<A> b;
    };

(This is accepted by g++.)

James Widman:

It would not be so helpful when used with overloaded function templates, for example:

    template <template <class> class TTP>  void f(); // #1
    template <                 class T  >  void f(); // #2

    template <class T> struct A { };

    struct C: A<int> {
        void h(  ) {
            f<A>(); //  #1?  #2?  Substitution failure?
        }
    };

(See also issue 602.)

Proposed resolution (November, 2010):

Change 14.6.1 [temp.local] paragraphs 1-5 as follows:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9 [class]). The injected-class-name can be used with or without a template-argument-list as a template-name or a type-name. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration it refers to the specified class template specialization, which could be the current specialization or another specialization. class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

Within the scope of a class template specialization or partial specialization, when the injected-class-name is not followed by a < used as a type-name, it is equivalent to the injected-class-name template-name followed by the template-arguments of the class template specialization or partial specialization enclosed in <>. [Example:

  template<template<class> class T> class A { };
  template<class T> class Y;
  template<> class Y<int> {
    Y* p;           // meaning Y<int>
    Y<char>* q;     // meaning Y<char>
    A<Y>* a;        // meaning A<::Y>
    class B {
      template<class> friend class Y;  // meaning ::Y
    };
  };

end example]

The injected-class-name of a class template or class template specialization can be used either with or without a template-argument-list as a template-name or a type-name wherever it is in scope. [Example:

  template <class T> struct Base {
    Base* p;
  };

  template <class T> struct Derived: public Base<T> {
    typename Derived::Base* p;    // meaning Derived::Base<T>
  };

  template<class T, template<class> class U = T::template Base> struct Third { };
  Third<Base<int>> t; // OK, default argument uses injected-class-name as a template

end example]

A lookup that finds an injected-class-name (10.2 [class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is followed by a template-argument-list used as a template-name, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [Example:

  template <class T> struct Base { };
  template <class T> struct Derived: Base<int>, Base<char> {
    typename Derived::Base b;             // error: ambiguous
    typename Derived::Base<double> d;     // OK
  };

end example]

When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used without a template-argument-list, it always refers to the class template itself and not a specialization of the template. [Example:...

This resolution also resolves issue 602.




1057. decltype and the current instantiation

Section: 14.6.2.1  [temp.dep.type]     Status: review     Submitter: Mike Miller     Date: 2010-03-18

According to 14.6.2.1 [temp.dep.type] paragraph 3,

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.

This would presumably include something like

    template<typename T> struct A {
        struct B { };
        A<decltype(T())>::B b;    // no typename
    };

However, this example is rejected by current implementations. Does this need to be clarified in the existing wording?

Notes from the November, 2010 meeting:

The example is not well-formed; if T is an rvalue reference type, for example, decltype(T()) is not equivalent to T.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1056.




1074. Value-dependent noexcept-expressions

Section: 14.6.2.3  [temp.dep.constexpr]     Status: review     Submitter: Jason Merrill     Date: 2010-06-04

It is not clear why a noexcept-expression is value-dependent if its operand is value-dependent. It would seem that the value of the expression depends only on the type of the operand, not its value.

Proposed resolution (November, 2010):

Delete “noexcept( expression )” from the list in 14.6.2.3 [temp.dep.constexpr] paragraph 3.




1088. Dependent non-type template arguments

Section: 14.6.2.3  [temp.dep.constexpr]     Status: review     Submitter: Jason Merrill     Date: 2010-06-28

Given

    template <const char *N> struct A { static const char *p; };
    template <class T>
       struct B { static const char c[1]; typedef A<B<T>::c> C; };
    template <class T>
       struct D { static const char c[1]; typedef A<c> C; };

14.6.2.4 [temp.dep.temp] says that B<T>::c is dependent because it is used as a non-integral non-type template argument and it is a qualified-id with a nested-name-specifier that names a dependent type.

There doesn't seem to be anything to say that c in the definition of D<T>::C is dependent, which suggests that D<T>::C is the same type for all T, which is clearly false.

Instead of this special rule in 14.6.2.4 [temp.dep.temp], 14.6.2.3 [temp.dep.constexpr] should say that the address of a member of a dependent type is value-dependent, regardless of whether the address is written with a qualified-id.

Proposed resolution (November, 2010):

  1. Add a new paragraph at the end of 14.6.2.3 [temp.dep.constexpr]:

  2. An id-expression is value-dependent if it names a member of an unknown specialization.
  3. Change 14.6.2.4 [temp.dep.temp] paragraphs 2-3 as follows:

  4. An integral A non-type template-argument is dependent if its type is dependent or the constant expression it specifies is value-dependent.

    A non-integral Furthermore, a non-type template-argument is dependent if its type is dependent or it has either of the following forms

    and contains a nested-name-specifier which specifies a class-name that names a dependent type the corresponding non-type template-parameter is of reference or pointer type and the template-argument designates or points to a member of the current instantiation or a member of a dependent type.




1196. Definition required for explicit instantiation after explicit specialization?

Section: 14.7.2  [temp.explicit]     Status: review     Submitter: Mike Miller     Date: 2010-09-08

According to 14.7.2 [temp.explicit] paragraph 4,

For a given set of template parameters, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect.

However, that does not appear to negate the requirements of paragraph 3 that a definition of the entity being instantiated must be in scope. Consequently, the following would appear to be ill-formed, even though there is no real need for the definition of C:

    template<typename T> class C;
    template<> class C<int> { };
    template class C<int>;

Proposed resolution (November, 2010):

Change 14.7.2 [temp.explicit] paragraphs 3-4 as follows:

A declaration of a function template shall be in scope at the point of the explicit instantiation of the function template. A definition of the class or class template containing a member function template shall be in scope at the point of the explicit instantiation of the member function template. A definition of a class template or class member template shall be in scope at the point of the explicit instantiation of the class template or class member template. A definition of a class template shall be in scope at the point of an explicit instantiation of a member function or a static data member of the class template. A definition of a member class of a class template shall be in scope at the point of an explicit instantiation of the member class. A declaration of a function template, a member function or static data member of a class template, or a member function template of a class or class template shall precede an explicit instantiation of that entity. A definition of a class template, a member class of a class template, or a member class template of a class or class template shall precede an explicit instantiation of that entity, unless the explicit instantiation is preceded by an explicit specialization of the entity with the same template arguments. If the declaration of the explicit instantiation names an implicitly-declared special member function (Clause 12 [special]), the program is ill-formed.

For a given set of template parameters arguments, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect. Otherwise...




531. Defining members of explicit specializations

Section: 14.7.3  [temp.expl.spec]     Status: review     Submitter: Mike Miller     Date: 1 October 2005

The Standard does not fully describe the syntax to be used when a member of an explicitly-specialized member class or member class template is defined in namespace scope. 14.7.3 [temp.expl.spec] paragraph 4 says that the “explicit specialization syntax” (presumably referring to “template<>”) is not used in defining a member of an explicit specialization when a class template is explicitly specialized as a class. However, nothing is said anywhere about how to define a member of a specialization when:

  1. the entity being specialized is a class (member of a template class) rather than a class template.

  2. the result of the specialization is a class template rather than a class (cf 14.7.3 [temp.expl.spec] paragraph 18, which describes this case as a “member template that... remain[s] unspecialized”).

(See paper J16/05-0148 = WG21 N1888 for further details, including a survey of existing implementation practice.)

Notes from the October, 2005 meeting:

The CWG felt that the best approach, balancing consistency with implementation issues and existing practice, would be to require that template<> be used when defining members of all explicit specializations, including those currently covered by 14.7.3 [temp.expl.spec] paragraph 4.

Proposed resolution (February, 2010):

Change 14.7.3 [temp.expl.spec] paragraph 5 as follows:

...The definition of an explicitly specialized class is unrelated to the definition of a generated specialization. That is, its members need not have the same names, types, etc. as the members of a generated specialization. Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the syntax for explicit specialization using the same template<> prefix(es) as the explicitly specialized class. [Example:

    template<class T> struct A {
      void f(T) { /* ... */ }
      struct B { /* ... */ };
      template<class U> struct C { /* ... */ };
    };
    template<> struct A<int> {
      void f(int);
      struct B;
      template<class U> struct C;
    };
    void h() {
      A<int> a;
      a.f(16); // A<int>::f must be defined somewhere
    }
    // explicit specialization syntax not used for a member of
    // explicitly specialized class template specialization
    // members of explicitly specialized classes are defined using
    //the same syntax as the explicitly specialized class:
    template<> void A<int>::f(int) { /* ... */ }
    template<> struct A<int>::B { /* ... */ };
    template<> template<class T> struct A<int>::C { /* ... */ };

end example]

Note (June, 2010):

Because the survey of implementations on which the CWG relied in reaching this resolution is quite old, a new survey of current practice is needed.




1178. Deduction failure matching placement new

Section: 14.8.2.6  [temp.deduct.decl]     Status: review     Submitter: Mike Miller     Date: 2010-08-19

The new wording added by issue 873 says,

...This is also done to determine whether a function template specialization matches a placement operator new (3.7.4.2 [basic.stc.dynamic.deallocation], 5.3.4 [expr.new])... If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (14.5.6.2 [temp.func.order]), deduction fails and the declaration is ill-formed.

The statement describing the consequence of deduction failure (“the declaration is ill-formed”) does not apply to the case when deduction is being performed for placement operator delete, as there is no declaration involved. It may not be necessary to describe what happens when deduction fails in that case, but at least the wording should be tweaked to limit the conclusion to declarative contexts.

Proposed resolution (November, 2010):

Change 14.8.2.6 [temp.deduct.decl] paragraphs 1-2 as follows:

In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations (14.7.2 [temp.explicit]), explicit specializations (14.7.3 [temp.expl.spec]), and certain friend declarations (14.5.4 [temp.friend]). This is also done to determine whether a deallocation function template specialization matches a placement operator new (3.7.4.2 [basic.stc.dynamic.deallocation], 5.3.4 [expr.new]). In all these cases, P is the type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 5.3.4 [expr.new]. The deduction is done as described in 14.8.2.5 [temp.deduct.type].

If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (14.5.6.2 [temp.func.order]), deduction fails and, in the declaration cases, the declaration program is ill-formed.




388. Catching base*& from a throw of derived*

Section: 15.3  [except.handle]     Status: review     Submitter: John Spicer     Date: 28 Oct 2002

I have a question about exception handling with respect to derived to base conversions of pointers caught by reference.

What should the result of this program be?

  struct S             {};
  struct SS : public S {};

  int main()
  {
  	SS ss;
  	int result = 0;
  	try
  	{
  		throw &ss; // throw object has type SS*
  		           // (pointer to derived class)
  	}
  	catch (S*& rs) // (reference to pointer to base class)
  	{
  		result = 1;
  	}
  	catch (...)
  	{
  		result = 2;
  	}
  	return result;
  }

The wording of 15.3 [except.handle] paragraph 3 would seem to say that the catch of S*& does not match and so the catch ... would be taken.

All of the compilers I tried (EDG, g++, Sun, and Microsoft) used the catch of S*& though.

What do we think is the desired behavior for such cases?

My initial reaction is that this is a bug in all of these compilers, but the fact that they all do the same thing gives me pause.

On a related front, if the handler changes the parameter using the reference, what is caught by a subsequent handler?

  extern "C" int printf(const char *, ...);
  struct S             {};
  struct SS : public S {};
  SS ss;

  int f()
  {
  	try
  	{
  		throw &ss;
  	}
  	catch (S*& rs) // (reference to pointer to base class)
  	{
  		rs = 0;
  		throw;
  	}
  	catch (...)
  	{
  	}
  	return 0;
  }

  int main()
  {
  	try { f(); }
  	catch (S*& rs) {
  		printf("rs=%p, &ss=%p\n", rs, &ss);
  	}
  }

EDG, g++, and Sun all catch the original (unmodified) value. Microsoft catches the modified value. In some sense the EDG/g++/Sun behavior makes sense because the later catch could catch the derived class instead of the base class, which would be difficult to do if you let the catch clause update the value to be used by a subsequent catch.

But on this non-pointer case, all of the compilers later catch the modified value:

  extern "C" int printf(const char *, ...);
  int f()
  {
  	try
  	{
  		throw 1;
  	}
  	catch (int& i)
  	{
  		i = 0;
  		throw;
  	}
  	catch (...)
  	{
  	}
  	return 0;
  }

  int main()
  {
  	try { f(); }
  	catch (int& i) {
  		printf("i=%p\n", i);
  	}
  }

To summarize:

  1. Should "base*const&" be able to catch a "derived*"? The current standard seems to say "no" but parallels to how calls work, and existing practice, suggest that the answer should be "yes".
  2. Should "base*&" be able to catch a "derived*". Again, the standard seems seems to say "no". Parallels to how calls work still suggest "no", but existing practice suggests "yes".
  3. If either of the above is "yes", what happens if you modify the pointer referred to by the reference. This requires a cast to remove const for case #2.
  4. On a related front, if you catch "derived*&" when a "derived*" is thrown, what happens if you modify the pointer referred to by the reference? EDG/g++/Sun still don't modify the underlying value that would be caught by a rethrow in this case. This case seems like it should be the same as the "int&" example above, but is not on the three compilers mentioned.

(See also issue 729.)

Notes from the October, 2009 meeting:

The consensus of the CWG was that it should not be possible to catch a pointer to a derived class using a reference to a base class pointer, and that a handler that takes a reference to non-const pointer should allow the pointer to be modified by the handler.

Proposed resolution (February, 2010):

Change 15.3 [except.handle] paragraph 3 as follows:

A handler is a match for an exception object of type E if

(This resolution also resolves issue 729.)




729. Qualification conversions and handlers of reference-to-pointer type

Section: 15.3  [except.handle]     Status: review     Submitter: John Spicer     Date: 6 October, 2008

Given the following example:

    int f() {
        try { /* ... */ }
        catch(const int*&) {
            return 1;
        }
        catch(int*&) {
            return 2;
        }
        return 3;
    }

can f() return 2? That is, does an int* exception object match a const int*& handler?

According to 15.3 [except.handle] paragraph 3, it does not:

A handler is a match for an exception object of type E if

Only the third bullet allows qualification conversions, but only the first bullet applies to a handler of reference-to-pointer type. This is consistent with how other reference bindings work; for example, the following is ill-formed:

    int* p;
    const int*& r = p;

(The consistency is not complete; the reference binding would be permitted if r had type const int* const &, but a handler of that type would still not match an int* exception object.)

However, implementation practice seems to be in the other direction; both EDG and g++ do match an int* with a const int*&, and the Microsoft compiler issues an error for the presumed hidden handler in the code above. Should the Standard be changed to reflect existing practice?

(See also issue 388.)

Notes from the October, 2009 meeting:

The CWG agreed that matching the exception object with a handler should, to the extent possible, mimic ordinary reference binding in cases like this.

Proposed resolution (February, 2010):

This issue is resolved by the resolution of issue 388.




1216. Exceptions “allowed” by a noexcept-specification

Section: 15.4  [except.spec]     Status: review     Submitter: Jens Maurer     Date: 2010-11-02

According to 15.4 [except.spec] paragraph2 8 and 9,

A function is said to allow an exception of type E if its dynamic-exception-specification contains a type T for which a handler of type T would be a match (15.3 [except.handle]) for an exception of type E.

Whenever an exception is thrown and the search for a handler (15.3 [except.handle]) encounters the outermost block of a function with an exception-specification that does not allow the exception, then,

This does not define what it means for a noexcept-specification to allow an exception.

Proposed resolution (November, 2010):

Change 15.4 [except.spec] paragraph 8 as follows:

A function is said to allow an exception of type E if the constant-expression in its noexcept-specification evaluates to false or its dynamic-exception-specification contains a type T for which a handler of type T would be a match (15.3 [except.handle]) for an exception of type E.





Issues with "Drafting" Status


369. Are new/delete identifiers or preprocessing-op-or-punc?

Section: 2.5  [lex.pptoken]     Status: drafting     Submitter: Martin v. Loewis     Date: 30 July 2002

2.5 [lex.pptoken] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 2.13 [lex.operators] paragraph 1, it is unclear whether new is an identifier or a preprocessing-op-or-punc; likewise for delete. This is relevant to answer the question whether

#define delete foo

is a well-formed control-line, since that requires an identifier after the define token.

(See also issue 189.)




189. Definition of operator and punctuator

Section: 2.13  [lex.operators]     Status: drafting     Submitter: Mike Miller     Date: 20 Dec 1999

The nonterminals operator and punctuator in 2.7 [lex.token] are not defined. There is a definition of the nonterminal operator in 13.5 [over.oper] paragraph 1, but it is apparent that the two nonterminals are not the same: the latter includes keywords and multi-token operators and does not include the nonoverloadable operators mentioned in paragraph 3.

There is a definition of preprocessing-op-or-punc in 2.13 [lex.operators] , with the notation that

Each preprocessing-op-or-punc is converted to a single token in translation phase 7 (2.1).
However, this list doesn't distinguish between operators and punctuators, it includes digraphs and keywords (can a given token be both a keyword and an operator at the same time?), etc.

Suggested resolution:


  1. Change 13.5 [over.oper] to use the term overloadable-operator.
  2. Change 2.7 [lex.token] to use the term operator-token instead of operator (since there are operators that are keywords and operators that are composed of more than one token).
  3. Change 2.13 [lex.operators] to define the nonterminals operator-token and punctuator.

Additional note (April, 2005):

The resolution for this problem should also address the fact that sizeof and typeid (and potentially others like decltype that may be added in the future) are described in some places as “operators” but are not listed in 13.5 [over.oper] paragraph 3 among the operators that cannot be overloaded.

(See also issue 369.)




1210. Injection of elaborated-type-specifier in enumeration scope

Section: 3.3.2  [basic.scope.pdecl]     Status: drafting     Submitter: Johannes Schaub     Date: 2010-10-13

According to 3.3.2 [basic.scope.pdecl] paragraph 6,

for an elaborated-type-specifier of the form

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.

This should have been, but was not, updated when enumeration scope (3.3.8 [basic.scope.enum]) was added:

    enum class E {
        e = sizeof((struct S*)0)
    };

Presumably the name S belongs to the same scope as E, not the enumeration scope of E.




225. Koenig lookup and fundamental types

Section: 3.4.2  [basic.lookup.argdep]     Status: drafting     Submitter: Derek Inglis     Date: 26 Jan 2000

In discussing issue 197, the question arose as to whether the handling of fundamental types in argument-dependent lookup is actually what is desired. This question needs further discussion.




156. Name lookup for conversion functions

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Derek Inglis     Date: 18 Aug 1999

Paragraph 7 of 3.4.5 [basic.lookup.classref] says,

If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression).
Does this mean that the following example is ill-formed?
    struct A { operator int(); } a;
    void foo() {
      typedef int T;
      a.operator T(); // 1) error T is not found in the context
		      // of the class of the object expression?
    }
The second bullet in paragraph 1 of 3.4.3.1 [class.qual] says,
a conversion-type-id of an operator-function-id is looked up both in the scope of the class and in the context in which the entire postfix-expression occurs and shall refer to the same type in both contexts
How about:
    struct A { typedef int T; operator T(); };
    struct B : A { operator T(); } b;
    void foo() {
      b.A::operator T(); // 2) error T is not found in the context
			 // of the postfix-expression?
    }
Is this interpretation correct? Or was the intent for this to be an error only if T was found in both scopes and referred to different entities?

If the intent was for these to be errors, how do these rules apply to template arguments?

    template <class T1> struct A { operator T1(); }
    template <class T2> struct B : A<T2> {
      operator T2();
      void foo() {
	T2 a = A<T2>::operator T2(); // 3) error? when instantiated T2 is not
				     // found in the scope of the class
	T2 b = ((A<T2>*)this)->operator T2(); // 4) error when instantiated?
      }
    }

(Note bullets 2 and 3 in paragraph 1 of 3.4.3.1 [class.qual] refer to postfix-expression. It would be better to use qualified-id in both cases.)

Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).

Mike Miller: What's not clear to me in these examples is whether what is being looked up is T or int. Clearly the T has to be looked up somehow, but the "name" of a conversion function clearly involves the base (non-typedefed) type, not typedefs that might be used in a definition or reference (cf 3 [basic] paragraph 7 and 12.3 [class.conv] paragraph 5). (This is true even for types that must be written using typedefs because of the limited syntax in conversion-type-ids — e.g., the "name" of the conversion function in the following example

    typedef void (*pf)();
    struct S {
	operator pf();
    };
is S::operator void(*)(), even though you can't write its name directly.)

My guess is that this means that in each scope you look up the type named in the reference and form the canonical operator name; if the name used in the reference isn't found in one or the other scope, the canonical name constructed from the other scope is used. These names must be identical, and the conversion-type-id in the canonical operator name must not denote different types in the two scopes (i.e., the type might not be found in one or the other scope, but if it's found in both, they must be the same type).

I think this is all very vague in the current wording.




682. Missing description of lookup of template aliases

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 1 March, 2008

3.4.5 [basic.lookup.classref] does not mention template aliases as the possible result of the lookup but should do so.




1089. Template parameters in member selections

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2010-06-29

In an example like

    template<typename T> void f(T p)->decltype(p.T::x);

The nested-name-specifier T:: looks like it refers to the template parameter. However, if this is instantiated with a type like

    struct T { int x; };
    struct S: T { };

the reference will be ambiguous, since it is looked up in both the context of the expression, finding the template parameter, and in the class, finding the base class injected-class-name, and this could be a deduction failure. As a result, the same declaration with a different parameter name

    template<typename U> void f(U p)->decltype(p.U::x);

is, in fact, not a redeclaration because the two can be distinguished by SFINAE.

It would be better to add a new lookup rule that says that if a name in a template definition resolves to a template parameter, that name is not subject to further lookup at instantiation time.




1220. Looking up conversion-type-ids

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Mike Miller     Date: 2010-11-13

The resolution of issue 1111 changes 3.4.5 [basic.lookup.classref] paragraph 7 to read,

[A] conversion-type-id is first looked up in the class of the object expression and the name, if found and denotes a type, is used. Otherwise it is looked up in the context of the entire postfix-expression and the name shall denote a type.

The result of this specification is that a non-type member declaration in the class scope of the object expression will not be found (although it will hide a base class type member of the same name), but a non-type declaration in the context of the expression will be found (and make the program ill-formed).

This is inconsistent with the way other lookups are handled when they occur in a context that requires a type. For example, the lookup for a nested-name-specifier “considers only namespaces, types, and templates whose specializations are types” (3.4.3 [basic.lookup.qual] paragraph 1); the lookup for a name appearing in an elaborated-type-specifier is done “ignoring any non-type names that have been declared” (3.4.4 [basic.lookup.elab] paragraph 2); and in the lookup for a name in a base-type-specifier, “non-type names are ignored” (10 [class.derived] paragraph 2). The lookup for a conversion-type-id should be similar, and the wording in 3.4.5 [basic.lookup.classref] paragraph 7 adjusted accordingly.




426. Identically-named variables, one internally and one externally linked, allowed?

Section: 3.5  [basic.link]     Status: drafting     Submitter: Steve Adamczyk     Date: 2 July 2003

An example in 3.5 [basic.link] paragraph 6 creates two file-scope variables with the same name, one with internal linkage and one with external.

  static void f();
  static int i = 0;                       //1
  void g() {
          extern void f();                // internal linkage
          int i;                          //2: i has no linkage
          {
                  extern void f();        // internal linkage
                  extern int i;           //3: external linkage
          }
  }

Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.

Notes from October 2003 meeting:

We agree that this is an error. We propose to leave the example but change the comment to indicate that line //3 has undefined behavior, and elsewhere add a normative rule giving such a case undefined behavior.

Proposed resolution (October, 2005):

Change 3.5 [basic.link] paragraph 6 as indicated:

...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the behavior is undefined.

[Example:

    static void f();
    static int i = 0;            // 1
    void g () {
        extern void f ();        // internal linkage
        int i;                   // 2: i has no linkage
        {
            extern void f ();    // internal linkage
            extern int i;        // 3: external linkage
        }
    }

There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line //1 ), the object with automatic storage duration and no linkage introduced by the declaration on line //2, and the object with static storage duration and external linkage introduced by the declaration on line //3. Without the declaration at line //2, the declaration at line //3 would link with the declaration at line //1. But because the declaration with internal linkage is hidden, //3 is given external linkage, resulting in a linkage conflict.end example]

Notes frum the April 2006 meeting:

According to 3.5 [basic.link] paragraph 9, the two variables with linkage in the proposed example are not “the same entity” because they do not have the same linkage. Some other formulation will be needed to describe the relationship between those two variables.

Notes from the October 2006 meeting:

The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.




1027. Type consistency and reallocation of scalar types

Section: 3.8  [basic.life]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 2010-02-03

Is the following well-formed?

    int f() {
        int i = 3;
        new (&i) float(1.2);
        return i;
    }

The wording that is intended to prevent such shenanigans, 3.8 [basic.life] paragraphs 7-9, doesn't quite apply here. In particular, paragraph 7 reads,

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

The problem here is that this wording only applies “after the lifetime of an object has ended and before the storage which the object occupied is reused;” for an object of a scalar type, its lifetime only ends when the storage is reused or released (paragraph 1), so it appears that these restrictions cannot apply to such objects.

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1116.




1116. Aliasing of union members

Section: 3.8  [basic.life]     Status: drafting     Submitter: US     Date: 2010-08-02

N3092 comment US 27

Related to issue 1027, consider:

    int f() {
      union U { double d; } u1, u2;
      (int&)u1.d = 1;
      u2 = u1;
      return (int&)u2.d;
    }

Does this involve undefined behavior? 3.8 [basic.life] paragraph 4 seems to say that it's OK to clobber u1 with an int object. Then union assignment copies the object representation, possibly creating an int object in u2 and making the return statement well-defined. If this is well-defined, compilers are significantly limited in the assumptions they can make about type aliasing. On the other hand, the variant where U has an array of unsigned char member must be well-defined in order to support std::aligned_storage.

Suggested resolution: Clarify that this case is undefined, but that adding an array of unsigned char to union U would make it well-defined — if a storage location is allocated with a particular type, it should be undefined to create an object in that storage if it would be undefined to access the stored value of the object through the allocated type.

Proposed resolution (August, 2010):

  1. Change 3.8 [basic.life] paragraph 1 as follows:

  2. ...The lifetime of an object of type T begins when storage with the proper alignment and size for type T is obtained, and either:

    The lifetime of an object of type T ends...

  3. Change 3.8 [basic.life] paragraph 4 as follows:

  4. A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5 [expr.delete]) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior. If a program obtains storage for an object of a particular type A (e.g. with a variable definition or new-expression) and later reuses that storage for an object of another type B such that accessing the stored value of the B object through a glvalue of type A would have undefined behavior (3.10 [basic.lval]), the behavior is undefined. [Example:

      int i;
      (double&)i = 1.0; // undefined behavior
    
      struct S { unsigned char alignas(double) ar[sizeof (double)]; } s;
      (double&)s = 1.0; // OK, can access stored double through s because it has an unsigned char subobject
    

    end example]

  5. Change 3.10 [basic.lval] paragraph 10 as follows:

  6. If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined52:

This resolution also resolves issue 1027.




1059. Cv-qualified array types (with rvalues)

Section: 3.9.3  [basic.type.qualifier]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2010-03-20

In spite of the resolution of issue 112, the exact relationship between cv-qualifiers and array types is not clear. There does not appear to be a definitive normative statement answering the question of whether an array with a const-qualified element type is itself const-qualified; the statement in 3.9.3 [basic.type.qualifier] paragraph 5,

Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T,” where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types.

hints at an answer but is hardly decisive. For example, is the following example well-formed?

    template <class T> struct is_const {
        static const bool value = false;
    };
    template <class T> struct is_const<const T> {
        static const bool value = true;
    };

    template <class T> void f(T &) {
        char checker[is_const<T>::value];
    }

    int const arr[1] = {};

    int main() {
        f(arr);
    }

Also, when 3.10 [basic.lval] paragraph 4 says,

Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types.

does this apply to array rvalues, as it appears? That is, given

    struct S {
        const int arr[10];
    };

is the array rvalue S().arr an array of int or an array of const int?

(The more general question is, when the Standard refers to non-class types, should it be considered to include array types? Or perhaps only arrays of non-class types?)




636. Dynamic type of objects and aliasing

Section: 3.10  [basic.lval]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 23 May 2007

The aliasing rules given in 3.10 [basic.lval] paragraph 10 rely on the concept of “dynamic type.” The problem is that the dynamic type of an object often cannot be determined (or even sufficiently constrained) at the point at which an optimizer needs to be able to determine whether aliasing might occur or not. For example, consider the function

    void foo(int* p, double* q) {
        *p = 42;
        *q = 3.14;
    }

An optimizer, on the basis of the existing aliasing rules, might decide that an int* and a double* cannot refer to the same object and reorder the assignments. This reordering, however, could result in undefined behavior if the function foo is called as follows:

   void goo() {
      union {
         int i; 
         double d;
      } t;

      t.i = 12;

      foo(&t.i, &t.d);

      cout << t.d << endl;
   };

Here, the reference to t.d after the call to foo will be valid only if the assignments in foo are executed in the order in which they were written; otherwise, the union will contain an int object rather than a double.

One possibility would be to require that if such aliasing occurs, it be done only via member names and not via pointers.

Notes from the July, 2007 meeting:

This is the same issue as C's DR236. The CWG expressed a desire to address the issue the same way C99 does. The issue also occurs in C++ when placement new is used to end the lifetime of one object and start the lifetime of a different object occupying the same storage.




1090. Alignment of subobjects

Section: 3.11  [basic.align]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2010-06-23

The current wording of the Standard does not recognize the fact that the alignment of a complete object of a given type may be different from its alignment as a subobject. This arises in particular with virtual base classes. For example,

    struct B { long double d; };
    struct D: virtual B { char c; };

When D is a complete object, it will have a subobject of type B, which must be aligned appropriately for a long double. On the other hand, if D appears as a suboject of another object, the B subobject might be part of a different subobject, reducing the alignment requirement on the D subobject.

The Standard should make clear that it is the complete-object alignment that is being described, in parallel with the distinction between the size of a complete object and a subobject of the same type.




1211. Misaligned lvalues

Section: 3.11  [basic.align]     Status: drafting     Submitter: David Svoboda     Date: 2010-10-20

3.11 [basic.align] speaks of “alignment requirements,” and 3.7.4.1 [basic.stc.dynamic.allocation] requires the result of an allocation function to point to “suitably aligned” storage, but there is no explicit statement of what happens when these requirements are violated (presumably undefined behavior).




617. Lvalue-to-rvalue conversions of uninitialized char objects

Section: 4.1  [conv.lval]     Status: drafting     Submitter: Alan Stokes     Date: 6 February 2007

According to 4.1 [conv.lval] paragraph 1, applying the lvalue-to-rvalue conversion to any uninitialized object results in undefined behavior. However, character types are intended to allow any data, including uninitialized objects and padding, to be copied (hence the statements in 3.9.1 [basic.fundamental] paragraph 1 that “For character types, all bits of the object representation participate in the value representation” and in 3.10 [basic.lval] paragraph 15 that char and unsigned char types can alias any object). The lvalue-to-rvalue conversion should be permitted on uninitialized objects of character type without evoking undefined behavior.




170. Pointer-to-member conversions

Section: 4.11  [conv.mem]     Status: drafting     Submitter: Mike Stump     Date: 16 Sep 1999

The descriptions of explicit (5.2.9 [expr.static.cast] paragraph 9) and implicit (4.11 [conv.mem] paragraph 2) pointer-to-member conversions differ in two significant ways:

  1. In a static_cast, a conversion in which the class in the target pointer-to-member type is a base of the class in which the member is declared is permitted and required to work correctly, as long as the resulting pointer-to-member is eventually dereferenced with an object whose dynamic type contains the member. That is, the class of the target pointer-to-member type is not required to contain the member referred to by the value being converted. The specification of implicit pointer-to-member conversion is silent on this question.

    (This situation cannot arise in an implicit pointer-to-member conversion where the source value is something like &X::f, since you can only implicitly convert from pointer-to-base-member to pointer-to-derived-member. However, if the source value is the result of an explicit "up-cast," the target type of the conversion might still not contain the member referred to by the source value.)

  2. The target type in a static_cast is allowed to be more cv-qualified than the source type; in an implicit conversion, however, the cv-qualifications of the two types are required to be identical.

The first difference seems like an oversight. It is not clear whether the latter difference is intentional or not.

(See also issue 794.)




536. Problems in the description of id-expressions

Section: 5.1.1  [expr.prim.general]     Status: drafting     Submitter: Mike Miller     Date: 13 October 2005

There are at least a couple of problems in the description of the various id-expressions in 5.1.1 [expr.prim.general]:

  1. Paragraph 4 embodies an incorrect assumption about the syntax of qualified-ids:

    The operator :: followed by an identifier, a qualified-id, or an operator-function-id is a primary-expression.

    The problem here is that the :: is actually part of the syntax of qualified-id; consequently, “:: followed by... a qualified-id” could be something like “:: ::i,” which is ill-formed. Presumably this should say something like, “A qualified-id with no nested-name-specifier is a primary-expression.”

  2. More importantly, some kinds of id-expressions are not described by 5.1.1 [expr.prim.general]. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it covers:

    This treatment leaves unspecified all the non-identifier unqualified-ids (operator-function-id, conversion-function-id, and template-id), as well as (perhaps) “:: template-id” (it's not clear whether the “:: followed by a qualified-id” case is supposed to apply to template-ids or not). Note also that the proposed resolution of issue 301 slightly exacerbates this problem by removing the form of operator-function-id that contains a tmeplate-argument-list; as a result, references like “::operator+<X>” are no longer covered in 5.1.1 [expr.prim.general].




232. Is indirection through a null pointer undefined behavior?

Section: 5.3.1  [expr.unary.op]     Status: drafting     Submitter: Mike Miller     Date: 5 Jun 2000

At least a couple of places in the IS state that indirection through a null pointer produces undefined behavior: 1.9 [intro.execution] paragraph 4 gives "dereferencing the null pointer" as an example of undefined behavior, and 8.3.2 [dcl.ref] paragraph 4 (in a note) uses this supposedly undefined behavior as justification for the nonexistence of "null references."

However, 5.3.1 [expr.unary.op] paragraph 1, which describes the unary "*" operator, does not say that the behavior is undefined if the operand is a null pointer, as one might expect. Furthermore, at least one passage gives dereferencing a null pointer well-defined behavior: 5.2.8 [expr.typeid] paragraph 2 says

If the lvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value (4.10 [conv.ptr]), the typeid expression throws the bad_typeid exception (18.7.3 [bad.typeid]).

This is inconsistent and should be cleaned up.

Bill Gibbons:

At one point we agreed that dereferencing a null pointer was not undefined; only using the resulting value had undefined behavior.

For example:

    char *p = 0;
    char *q = &*p;

Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:

    char a[10];
    char *b = &a[10];   // equivalent to "char *b = &*(a+10);"

Both cases come up often enough in real code that they should be allowed.

Mike Miller:

I can see the value in this, but it doesn't seem to be well reflected in the wording of the Standard. For instance, presumably *p above would have to be an lvalue in order to be the operand of "&", but the definition of "lvalue" in 3.10 [basic.lval] paragraph 2 says that "an lvalue refers to an object." What's the object in *p? If we were to allow this, we would need to augment the definition to include the result of dereferencing null and one-past-the-end-of-array.

Tom Plum:

Just to add one more recollection of the intent: I was very happy when (I thought) we decided that it was only the attempt to actually fetch a value that creates undefined behavior. The words which (I thought) were intended to clarify that are the first three sentences of the lvalue-to-rvalue conversion, 4.1 [conv.lval]:

An lvalue (3.10 [basic.lval]) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

In other words, it is only the act of "fetching", of lvalue-to-rvalue conversion, that triggers the ill-formed or undefined behavior. Simply forming the lvalue expression, and then for example taking its address, does not trigger either of those errors. I described this approach to WG14 and it may have been incorporated into C 1999.

Mike Miller:

If we admit the possibility of null lvalues, as Tom is suggesting here, that significantly undercuts the rationale for prohibiting "null references" -- what is a reference, after all, but a named lvalue? If it's okay to create a null lvalue, as long as I don't invoke the lvalue-to-rvalue conversion on it, why shouldn't I be able to capture that null lvalue as a reference, with the same restrictions on its use?

I am not arguing in favor of null references. I don't want them in the language. What I am saying is that we need to think carefully about adopting the permissive approach of saying that it's all right to create null lvalues, as long as you don't use them in certain ways. If we do that, it will be very natural for people to question why they can't pass such an lvalue to a function, as long as the function doesn't do anything that is not permitted on a null lvalue.

If we want to allow &*(p=0), maybe we should change the definition of "&" to handle dereferenced null specially, just as typeid has special handling, rather than changing the definition of lvalue to include dereferenced nulls, and similarly for the array_end+1 case. It's not as general, but I think it might cause us fewer problems in the long run.

Notes from the October 2003 meeting:

See also issue 315, which deals with the call of a static member function through a null pointer.

We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error. An lvalue-to-rvalue conversion would give it undefined behavior.

Proposed resolution (October, 2004):

(Note: the resolution of issue 453 also resolves part of this issue.)

  1. Add the indicated words to 3.10 [basic.lval] paragraph 2:

    An lvalue refers to an object or function or is an empty lvalue (5.3.1 [expr.unary.op]).
  2. Add the indicated words to 5.3.1 [expr.unary.op] paragraph 1:

    The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points, if any. If the pointer is a null pointer value (4.10 [conv.ptr]) or points one past the last element of an array object (5.7 [expr.add]), the result is an empty lvalue and does not refer to any object or function. An empty lvalue is not modifiable. If the type of the expression is “pointer to T,” the type of the result is “T.” [Note: a pointer to an incomplete type (other than cv void) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to an rvalue, see 4.1 [conv.lval].—end note]
  3. Add the indicated words to 4.1 [conv.lval] paragraph 1:

    If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, or if the lvalue is an empty lvalue (5.3.1 [expr.unary.op]), a program that necessitates this conversion has undefined behavior.
  4. Change 1.9 [intro.execution] as indicated:

    Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer division by zero).

Note (March, 2005):

The 10/2004 resolution interacts with the resolution of issue 73. We added wording to 3.9.2 [basic.compound] paragraph 3 to the effect that a pointer containing the address one past the end of an array is considered to “point to” another object of the same type that might be located there. The 10/2004 resolution now says that it would be undefined behavior to use such a pointer to fetch the value of that object. There is at least the appearance of conflict here; it may be all right, but it at needs to be discussed further.

Notes from the April, 2005 meeting:

The CWG agreed that there is no contradiction between this direction and the resolution of issue 73. However, “not modifiable” is a compile-time concept, while in fact this deals with runtime values and thus should produce undefined behavior instead. Also, there are other contexts in which lvalues can occur, such as the left operand of . or .*, which should also be restricted. Additional drafting is required.

(See also issue 1102.)




901. Deleted operator delete

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: John Spicer     Date: 20 May, 2009

It is not clear from 5.3.4 [expr.new] whether a deleted operator delete is referenced by a new-expression in which there is no initialization or in which the initialization cannot throw an exception, rendering the program ill-formed. (The question also arises as to whether such a new-expression constitutes a “use” of the deallocation function in the sense of 3.2 [basic.def.odr].)

Notes from the July, 2009 meeting:

The rationale for defining a deallocation function as deleted would presumably be to prevent such objects from being freed. Treating the new-expression as a use of such a deallocation function would mean that such objects could not be created in the first place. There is already an exemption from freeing an object if “a suitable deallocation function [cannot] be found;” a deleted deallocation function should be treated similarly.




837. Constexpr functions and return braced-init-list

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Mike Miller     Date: 11 March, 2009

The body of a constexpr function is required by 7.1.5 [dcl.constexpr] paragraph 3 to be of the form

However, there does not seem to be any good reason for prohibiting the alternate return syntax involving a braced-init-list. The restriction should be removed.

Proposed resolution (March, 2010):

  1. Change 6.6.3 [stmt.return] paragraph 2 as follows:

  2. A return statement without an expression with neither an expression nor a braced-init-list can be used only in functions that do not return a value...
  3. Change 7.1.5 [dcl.constexpr] paragraph 3 bullets 4 and 5 as follows:

Notes from the March, 2010 meeting:

The new wording added in 5.19 [expr.const] in support of reference parameters for constexpr functions should also be considered to see whether additional changes are needed.




138. Friend declaration name lookup

Section: 7.3.1.2  [namespace.memdef]     Status: drafting     Submitter: Martin von Loewis     Date: 14 Jul 1999

7.3.1.2 [namespace.memdef] paragraph 3 says,

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:
    void foo();
    namespace A{
      using ::foo;
      class X{
	friend void foo();
      };
    }
Is the friend declaration a reference to ::foo or a different foo?

Part of the question involves determining the meaning of the word "synonym" in 7.3.3 [namespace.udecl] paragraph 1:

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.
Is "using ::foo;" the declaration of a function or not?

More generally, the question is how to describe the lookup of the name in a friend declaration.

John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.

Mike Miller: 3.4.1 [basic.lookup.unqual] paragraph 7 says:

A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a friend declaration, scopes outside of the innermost enclosing namespace scope are not considered.]
The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.

John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.

    void f(){}
    void g(){}
    class B {
        void g();
    };
    class A : public B {
        void f();
        friend void f(); // ::f not A::f
        friend void g(); // ::g not B::g
    };

Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 3.4.4 [basic.lookup.elab] paragraph 3:

    struct Base {
        struct Data;         // OK: declares nested Data
        friend class Data;   // OK: nested Data is a friend
    };

If the friend declaration is not a reference to ::foo, there is a related but separate question: does the friend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable) A::foo? Another part of the example in 3.4.4 [basic.lookup.elab] paragraph 3 is related:

    struct Data {
        friend struct Glob;  // OK: Refers to (as yet) undeclared Glob
                             // at global scope.
    };

John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.

(A somewhat similar question has been raised in connection with issue 36. Consider:

    namespace N {
        struct S { };
    }
    using N::S;
    struct S;          // legal?

According to 9.1 [class.name] paragraph 2,

A declaration consisting solely of class-key identifier ; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.

Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)

(See also issues 95, 136, 139, 143, 165, and 166, as well as paper J16/00-0006 = WG21 N1229.)




453. References may only bind to “valid” objects

Section: 8.3.2  [dcl.ref]     Status: drafting     Submitter: Gennaro Prota     Date: 18 Jan 2004

8.3.2 [dcl.ref] paragraph 4 says:

A reference shall be initialized to refer to a valid object or function. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior ...]

What is a "valid" object? In particular the expression "valid object" seems to exclude uninitialized objects, but the response to Core Issue 363 clearly says that's not the intent. This is an example (overloading construction on constness of *this) by John Potter, which I think is supposed to be legal C++ though it binds references to objects that are not initialized yet:

 struct Fun {
    int x, y;
    Fun (int x, Fun const&) : x(x), y(42) { }
    Fun (int x, Fun&) : x(x), y(0) { }
  };
  int main () {
    const Fun f1 (13, f1);
    Fun f2 (13, f2);
    cout << f1.y << " " << f2.y << "\n";
  }

Suggested resolution: Changing the final part of 8.3.2 [dcl.ref] paragraph 4 to:

A reference shall be initialized to refer to an object or function. From its point of declaration on (see 3.3.2 [basic.scope.pdecl]) its name is an lvalue which refers to that object or function. The reference may be initialized to refer to an uninitialized object but, in that case, it is usable in limited ways (3.8 [basic.life], paragraph 6) [Note: On the other hand, a declaration like this:
    int & ref = *(int*)0;
is ill-formed because ref will not refer to any object or function ]

I also think a "No diagnostic is required." would better be added (what about something like int& r = r; ?)

Proposed Resolution (October, 2004):

(Note: the following wording depends on the proposed resolution for issue 232.)

Change 8.3.2 [dcl.ref] paragraph 4 as follows:

A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of memory of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]

The name of a reference shall not be used in its own initializer. Any other use of a reference before it is initialized results in undefined behavior. [Example:

  int& f(int&);
  int& g();

  extern int& ir3;
  int* ip = 0;

  int& ir1 = *ip;     // undefined behavior: null pointer
  int& ir2 = f(ir3);  // undefined behavior: ir3 not yet initialized
  int& ir3 = g();
  int& ir4 = f(ir4);  // ill-formed: ir4 used in its own initializer
end example]

Rationale: The proposed wording goes beyond the specific concerns of the issue. It was noted that, while the current wording makes cases like int& r = r; ill-formed (because r in the initializer does not "refer to a valid object"), an inappropriate initialization can only be detected, if at all, at runtime and thus "undefined behavior" is a more appropriate treatment. Nevertheless, it was deemed desirable to continue to require a diagnostic for obvious compile-time cases.

It was also noted that the current Standard does not say anything about using a reference before it is initialized. It seemed reasonable to address both of these concerns in the same wording proposed to resolve this issue.

Notes from the April, 2005 meeting:

The CWG decided that whether to require an implementation to diagnose initialization of a reference to itself should be handled as a separate issue (504) and also suggested referring to “storage” instead of “memory” (because 1.8 [intro.object] defines an object as a “region of storage”).

Proposed Resolution (April, 2005):

(Note: the following wording depends on the proposed resolution for issue 232.)

Change 8.3.2 [dcl.ref] paragraph 4 as follows:

A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of storage of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]

Any use of a reference before it is initialized results in undefined behavior. [Example:

  int& f(int&);
  int& g();

  extern int& ir3;
  int* ip = 0;

  int& ir1 = *ip;     // undefined behavior: null pointer
  int& ir2 = f(ir3);  // undefined behavior: ir3 not yet initialized
  int& ir3 = g();
  int& ir4 = f(ir4);  // undefined behavior: ir4 used in its own initializer
end example]

Note (February, 2006):

The word “use” in the last paragraph of the proposed resolution was intended to refer to the description in 3.2 [basic.def.odr] paragraph 2. However, that section does not define what it means for a reference to be “used,” dealing only with objects and functions. Additional drafting is required to extend 3.2 [basic.def.odr] paragraph 2 to apply to references.

Additional note (May, 2008):

The proposed resolution for issue 570 adds wording to define “use” for references.




393. Pointer to array of unknown bound in template argument list in parameter

Section: 8.3.5  [dcl.fct]     Status: drafting     Submitter: Mark Mitchell     Date: 12 Dec 2002

EDG rejects this code:

  template <typename T>
  struct S {};

  void f (S<int (*)[]>);
G++ accepts it.

This is another case where the standard isn't very clear:

The language from 8.3.5 [dcl.fct] is:

If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T," the program is ill-formed.
Since "includes a type" is not a term defined in the standard, we're left to guess what this means. (It would be better if this were a recursive definition, the way a type theoretician would do it: )

Notes from April 2003 meeting:

We agreed that the example should be allowed.




1214. Kinds of initializers

Section: 8.5  [dcl.init]     Status: drafting     Submitter: Johannes Schaub     Date: 2010-10-30

8.5 [dcl.init] paragraph 16 describes three kinds of initializers: a single expression, a braced-init-list, and a parenthesized list of expressions. It is not clear which, if any, of these categories is the appropriate description for an initializer like

    T t( { 1, 2 } )

and thus not clear which of the bullets in the list applies.




233. References vs pointers in UDC overload resolution

Section: 8.5.3  [dcl.init.ref]     Status: drafting     Submitter: Matthias Meixner     Date: 9 Jun 2000

There is an inconsistency in the handling of references vs pointers in user defined conversions and overloading. The reason for that is that the combination of 8.5.3 [dcl.init.ref] and 4.4 [conv.qual] circumvents the standard way of ranking conversion functions, which was probably not the intention of the designers of the standard.

Let's start with some examples, to show what it is about:

    struct Z { Z(){} };

    struct A {
       Z x;

       operator Z *() { return &x; }
       operator const Z *() { return &x; }
    };

    struct B {
       Z x;

       operator Z &() { return x; }
       operator const Z &() { return x; }
    };

    int main()
    {
       A a;
       Z *a1=a;
       const Z *a2=a; // not ambiguous

       B b;
       Z &b1=b;
       const Z &b2=b; // ambiguous
    }

So while both classes A and B are structurally equivalent, there is a difference in operator overloading. I want to start with the discussion of the pointer case (const Z *a2=a;): 13.3.3 [over.match.best] is used to select the best viable function. Rule 4 selects A::operator const Z*() as best viable function using 13.3.3.2 [over.ics.rank] since the implicit conversion sequence const Z* -> const Z* is a better conversion sequence than Z* -> const Z*.

So what is the difference to the reference case? Cv-qualification conversion is only applicable for pointers according to 4.4 [conv.qual]. According to 8.5.3 [dcl.init.ref] paragraphs 4-7 references are initialized by binding using the concept of reference-compatibility. The problem with this is, that in this context of binding, there is no conversion, and therefore there is also no comparing of conversion sequences. More exactly all conversions can be considered identity conversions according to 13.3.3.1.4 [over.ics.ref] paragraph 1, which compare equal and which has the same effect. So binding const Z* to const Z* is as good as binding const Z* to Z* in terms of overloading. Therefore const Z &b2=b; is ambiguous. [13.3.3.1.4 [over.ics.ref] paragraph 5 and 13.3.3.2 [over.ics.rank] paragraph 3 rule 3 (S1 and S2 are reference bindings ...) do not seem to apply to this case]

There are other ambiguities, that result in the special treatment of references: Example:

    struct A {int a;};
    struct B: public A { B() {}; int b;};

    struct X {
       B x;
       operator A &() { return x; }
       operator B &() { return x; }
    };

    main()
    {
       X x;
       A &g=x; // ambiguous
    }

Since both references of class A and B are reference compatible with references of class A and since from the point of ranking of implicit conversion sequences they are both identity conversions, the initialization is ambiguous.

So why should this be a defect?

So overall I think this was not the intention of the authors of the standard.

So how could this be fixed? For comparing conversion sequences (and only for comparing) reference binding should be treated as if it was a normal assignment/initialization and cv-qualification would have to be defined for references. This would affect 8.5.3 [dcl.init.ref] paragraph 6, 4.4 [conv.qual] and probably 13.3.3.2 [over.ics.rank] paragraph 3.

Another fix could be to add a special case in 13.3.3 [over.match.best] paragraph 1.




1005. Qualified name resolution in member functions of class templates

Section: 9.3.1  [class.mfct.non-static]     Status: drafting     Submitter: Doug Gregor     Date: 2009-11-19

It's not clear how lookup of a non-dependent qualified name should be handled in a non-static member function of a class template. For example,

    struct A {
      int f(int);
      static int f(double);
    };

    struct B {};

    template<typename T> struct C : T {
      void g() {
        A::f(0);
      }
    };

The call to A::f inside C::g() appears non-dependent, so one might expect that it would be bound at template definition time to A::f(double). However, the resolution for issue 515 changed 9.3.1 [class.mfct.non-static] paragraph 3 to transform an id-expression to a member access expression using (*this). if lookup resolves the name to a non-static member of any class, making the reference dependent. The result is that if C is instantiated with A, A::f(int) is called; if C is instantiated with B, the call is ill-formed (the call is transformed to (*this).A::f(0), and there is no A subobject in C<B>). Both these results seem unintuitive.

(See also issue 1017.)

Notes from the November, 2010 meeting:

The CWG agreed that the resolution of issue 515 was ill-advised and should be reversed.


1017. Member access transformation in unevaluated operands

Section: 9.3.1  [class.mfct.non-static]     Status: drafting     Submitter: Johannes Schaub     Date: 2009-12-30

The following innocuous-appearing code is currently ill-formed:

    struct A {
        int a;
    };

    struct B {
        void f() {
            decltype(A::a) i;    // ill-formed
        }
    };

The reason is that, according to 9.3.1 [class.mfct.non-static] paragraph 3, the reference to A::a is transformed into (*this).A::a, and there is no A subobject of B. It would seem reasonable to suppress this transformation in unevaluated operands, where a reference to a non-static member is permitted without an object expression.

(See also issue 1005.)

Notes from the November, 2010 meeting:

The CWG agreed that the resolution of issue 515 was ill-advised and should be reversed.


580. Access in template-parameters of member and friend definitions

Section: 11  [class.access]     Status: drafting     Submitter: John Spicer     Date: 16 May 2006

The resolution of issue 372 leaves unclear whether the following are well-formed or not:

    class C {
        typedef int I;                // private
        template <int> struct X;
        template <int> friend struct Y;
    }

    template <C::I> struct C::X { };  // C::I accessible to member?

    template <C::I> struct Y { };     // C::I accessible to friend?

Presumably the answer to both questions is “yes,” but the new wording does not address template-parameters.

Proposed resolution (June, 2008):

Change 11 [class.access] paragraph 6 as follows:

...For purposes of access control, the base-specifiers of a class, the template-parameters of a template-declaration, and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class...

Notes from the September, 2008 meeting:

The proposed resolution preserves the word “scope” as a holdover from the original specification prior to issue 372, which intended to change access determination from a scope-based model to an entity-based model. The resolution should eliminate all references to scope and simply use the entity-based model.

(See also issue 718.)

Proposed resolution (February, 2010):

Change 11 [class.access] paragraphs 6-7 as follows:

All access controls in Clause 11 [class.access] affect the ability to access a class member name from a declaration of a particular scope entity, including references appearing in those parts of the declaration that precede the name of the entity being declared and implicit references to constructors, conversion functions, and destructors involved in the creation and destruction of a static data member. 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, access controls apply as usual to member names accessed as part of a function return type, even though it is not possible to determine the access privileges of that use without first parsing the rest of the function declarator. Similarly, access control for implicit calls to the constructors, the conversion functions, or the destructor called to create and destroy a static data member is performed as if these calls appeared in the scope of the member's class. [Example:

  class A {
    typedef int I;    // private member
    I f();
    friend I g(I);
    static I x;
    template<int> struct X;
    template<int> friend struct Y;
  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;
  template<A::I> struct A::X { };
  template<A::I> struct Y { };

  struct D: A::B, A { };

Here, all the uses of A::I are well-formed because A::f and, A::x, and A::X are members of class A and g is a friend and Y are friends 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 checking of base-specifiers must be deferred until the entire base-specifier-list has been seen. —end example]




952. Insufficient description of “naming class”

Section: 11.2  [class.access.base]     Status: drafting     Submitter: James Widman     Date: 7 August, 2009

The access rules in 11.2 [class.access.base] do not appear to handle references in nested classes and outside of nonstatic member functions correctly. For example,

    struct A {
        typedef int I;    // public
    };
    struct B: private A { };
    struct C: B {
        void f() {
            I i1;         // error: access violation
        }
        I i2;             // OK
        struct D {
            I i3;         // OK
            void g() {
                I i4;     // OK
            }
        };
    };

The reason for this discrepancy is that the naming class in the reference to I is different in these cases. According to 11.2 [class.access.base] paragraph 5,

The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found.

In the case of i1, the reference to I is subject to the transformation described in 9.3.1 [class.mfct.non-static] paragraph 3:

Similarly during name lookup, when an unqualified-id (5.1 [expr.prim]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id (5.1 [expr.prim]) in which the nested-name-specifier names the class of the member function.

As a result, the reference to I in the declaration of i1 is transformed to C::I, so that the naming class is C, and I is inacessible in C. In the remaining cases, however, the transformation does not apply. Thus, the naming class of I in these references is A, and I is publicly accessible in A.

Presumably either the definition of “naming class” must be changed or the transformation of unqualified-ids must be broadened to include all uses within the scope of a class and not just within nonstatic member functions (and following the declarator-id in the definition of a static member, per 9.4 [class.static] paragraph 4).




472. Casting across protected inheritance

Section: 11.5  [class.protected]     Status: drafting     Submitter: Mike Miller     Date: 16 Jun 2004

Does the restriction in 11.5 [class.protected] apply to upcasts across protected inheritance, too? For instance,

    struct B {
        int i;
    };
    struct I: protected B { };
    struct D: I {
        void f(I* ip) {
            B* bp = ip;    // well-formed?
            bp->i = 5;     // aka "ip->i = 5;"
        }
    };

I think the rationale for the 11.5 [class.protected] restriction applies equally well here — you don't know whether ip points to a D object or not, so D::f can't be trusted to treat the protected B subobject consistently with the policies of its actual complete object type.

The current treatment of “accessible base class” in 11.2 [class.access.base] paragraph 4 clearly makes the conversion from I* to B* well-formed. I think that's wrong and needs to be fixed. The rationale for the accessibility of a base class is whether “an invented public member” of the base would be accessible at the point of reference, although we obscured that a bit in the reformulation; it seems to me that the invented member ought to be considered a non-static member for this purpose and thus subject to 11.5 [class.protected].

(See also issues 385 and 471.).

Notes from October 2004 meeting:

The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.5 [class.protected].

Proposed resolution (February, 2010):

Change 11.2 [class.access.base] paragraph 4 as follows:

A base class B of N is accessible at R, if

[Example:

    class B {
    public:
      int m;
    };

    class S: private B {
      friend class N;
    };
    class N: private S {
      void f() {
        B* p = this;    // OK because class S satisfies the fourth condition
                        // above: B is a base class of N accessible in f() because
                        // B is an accessible base class of S and S is an accessible
                        // base class of N.
      }
    };

    class N2: protected B { };

    class P2: public N2 {
      void f2(N2* n2p) {
        B* bp = n2p;    // error: invented member would be protected and naming
                        // class N2 not the same as or derived from the referencing
                        // class P2 (cf 11.5 [class.protected])
      }
    };

end example]




399. Destructor lookup redux

Section: 12.4  [class.dtor]     Status: drafting     Submitter: John Spicer     Date: 17 Jan 2003

Mark Mitchell raised a number of issues related to the resolution of issue 244 and of destructor lookup in general.

Issue 244 says:

... in a qualified-id of the form: the second class-name is looked up in the same scope as the first.

But if the reference is "p->X::~X()", the first class-name is looked up in two places (normal lookup and a lookup in the class of p). Does the new wording mean:

  1. You look up the second class-name in the scope that you found the first one.
  2. You look up the second class-name using the same kind of lookup that found the first one (normal vs. class).
  3. If you did a dual lookup for the first you do a dual lookup for the second.

This is a test case that illustrates the issue:

  struct A {
    typedef A C;
  };

  typedef A B;

  void f(B* bp) {
    bp->B::~B();  // okay B found by normal lookup
    bp->C::~C();  // okay C found by class lookup
    bp->B::~C();  // B found by normal lookup C by class -- okay?
    bp->C::~B();  // C found by class lookup B by normal -- okay?
  }

A second issue concerns destructor references when the class involved is a template class.

  namespace N {
    template <typename T> struct S {
      ~S();
    };
  }

  void f(N::S<int>* s) {
    s->N::S<int>::~S();
  }

The issue here is that the grammar uses "~class-name" for destructor names, but in this case S is a template name when looked up in N.

Finally, what about cases like:

  template <typename T> void f () {
    typename T::B x;
    x.template A<T>::template B<T>::~B();
  }

When parsing the template definition, what checks can be done on "~B"?

Sandor Mathe adds :

The standard correction for issue 244 (now in DR status) is still incomplete.

Paragraph 5 of 3.4.3 [basic.lookup.qual] is not applicable for p->T::~T since there is no nested-name-specifier. Section 3.4.5 [basic.lookup.classref] describes the lookup of p->~T but p->T::~T is still not described. There are examples (which are non-normative) that illustrate this sort of lookup but they still leave questions unanswered. The examples imply that the name after ~ should be looked up in the same scope as the name before the :: but it is not stated. The problem is that the name to the left of the :: can be found in two different scopes. Consider the following:

  struct S {
    struct C { ~C() { } };
  };

  typedef S::C D;

  int main() {
    D* p;
    p->C::~D();  // valid?
  }

Should the destructor call be valid? If there were a nested name specifier, then D should be looked for in the same scope as C. But here, C is looked for in 2 different ways. First, it is searched for in the type of the left hand side of -> and it is also looked for in the lexical context. It is found in one or if both, they must match. So, C is found in the scope of what p points at. Do you only look for D there? If so, this is invalid. If not, you would then look for D in the context of the expression and find it. They refer to the same underlying destructor so this is valid. The intended resolution of the original defect report of the standard was that the name before the :: did not imply a scope and you did not look for D inside of C. However, it was not made clear whether this was to be resolved by using the same lookup mechanism or by introducing a new form of lookup which is to look in the left hand side if that is where C was found, or in the context of the expression if that is where C was found. Of course, this begs the question of what should happen when it is found in both? Consider the modification to the above case when C is also found in the context of the expression. If you only look where you found C, is this now valid because it is in 1 of the two scopes or is it invalid because C was in both and D is only in 1?

  struct S {
    struct C { ~C() { } };
  };

  typedef S::C D;
  typedef S::C C;

  int main() {
    D* p;
    p->C::~D();  // valid?
  }

I agree that the intention of the committee is that the original test case in this defect is broken. The standard committee clearly thinks that the last name before the last :: does not induce a new scope which is our current interpretation. However, how this is supposed to work is not defined. This needs clarification of the standard.

Martin Sebor adds this example (September 2003), along with errors produced by the EDG front end:

namespace N {
    struct A { typedef A NA; };
    template <class T> struct B { typedef B NB; typedef T BT; };
    template <template <class> class T> struct C { typedef C NC; typedef T<A> CA; };
}

void foo (N::A *p)
{
    p->~NA ();
    p->NA::~NA ();
}

template <class T>
void foo (N::B<T> *p)
{
    p->~NB ();
    p->NB::~NB ();
}

template <class T>
void foo (typename N::B<T>::BT *p)
{
    p->~BT ();
    p->BT::~BT ();
}

template <template <class> class T>
void foo (N::C<T> *p)
{
    p->~NC ();
    p->NC::~NC ();
}

template <template <class> class T>
void foo (typename N::C<T>::CA *p)
{
    p->~CA ();
    p->CA::~CA ();
}

Edison Design Group C/C++ Front End, version 3.3 (Sep  3 2003 11:54:55)
Copyright 1988-2003 Edison Design Group, Inc.

"t.cpp", line 16: error: invalid destructor name for type "N::B<T>"
      p->~NB ();
          ^

"t.cpp", line 17: error: qualifier of destructor name "N::B<T>::NB" does not
          match type "N::B<T>"
      p->NB::~NB ();
              ^

"t.cpp", line 30: error: invalid destructor name for type "N::C<T>"
      p->~NC ();
          ^

"t.cpp", line 31: error: qualifier of destructor name "N::C<T>::NC" does not
          match type "N::C<T>"
      p->NC::~NC ();
              ^

4 errors detected in the compilation of "t.cpp".

John Spicer: The issue here is that we're unhappy with the destructor names when doing semantic analysis of the template definitions (not during an instantiation).

My personal feeling is that this is reasonable. After all, why would you call p->~NB for a class that you just named as N::B<T> and you could just say p->~B?

Additional note (September, 2004)

The resolution for issue 244 removed the discussion of p->N::~S, where N is a namespace-name. However, the resolution did not make this construct ill-formed; it simply left the semantics undefined. The meaning should either be defined or the construct made ill-formed.

See also issues 305 and 466.




1202. Calling virtual functions during destruction

Section: 12.7  [class.cdtor]     Status: drafting     Submitter: Bjarne Stroustrup     Date: 2010-09-21

According to 12.7 [class.cdtor] paragraph 4,

Member functions, including virtual functions (10.3 [class.virtual]), can be called during construction or destruction (12.6.2 [class.base.init]). When a virtual function is called directly or indirectly from a constructor (including the mem-initializer or brace-or-equal-initializer for a non-static data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor's own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor's class, or overriding it in one of the other base classes of the most derived object (1.8 [intro.object]).

This is clear regarding virtual functions called during the initialization of a class's members, but it does not specifically address the polymorphic behavior of the class during the destruction of the members. Presumably the behavior during destruction should be the exact inverse of that of the constructor, i.e., the class's virtual functions should still be called during member destruction.

In addition, the wording

If the virtual function call uses an explicit class member access (5.2.5 [expr.ref]) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor's own class or one of its bases, the result of the call is undefined.

should be clarified that “refers to the object under construction” does not include referring to member subobjects but only to base or more-derived classes of the class under construction or destruction.




1082. Implicit copy function if subobject has none?

Section: 12.8  [class.copy]     Status: drafting     Submitter: Jason Merrill     Date: 2010-06-22

It seems odd to have an implicitly declared copy constructor (and the same for the copy assignment operator) if one of the subobjects does not have one. For example,

    struct A {
       A();
       A(A&&);
    };

    struct B: A { };

    B b;
    B b2(b); // error when implicitly defining B(B&), should not be declared

If we don't declare it in that case, we need to decide what happens if one base has only a move constructor and another has only a copy constructor.

Notes from the November, 2010 meeting:

The consensus of the CWG was to change the behavior so that all classes have a declaration of a copy constructor, but that it is defined as deleted in the cases where the declaration is omitted by the current rules.




260. User-defined conversions and built-in operator=

Section: 13.6  [over.built]     Status: drafting     Submitter: Scott Douglas     Date: 4 Nov 2000

According to the Standard (although not implemented this way in most implementations), the following code exhibits non-intuitive behavior:

  struct T {
    operator short() const;
    operator int() const;
  };

  short s;

  void f(const T& t) {
    s = t;  // surprisingly calls T::operator int() const
  }

The reason for this choice is 13.6 [over.built] paragraph 18:

For every triple (L, VQ, R), where L is an arithmetic type, VQ is either volatile or empty, and R is a promoted arithmetic type, there exist candidate operator functions of the form

Because R is a "promoted arithmetic type," the second argument to the built-in assignment operator is int, causing the unexpected choice of conversion function.

Suggested resolution: Provide built-in assignment operators for the unpromoted arithmetic types.

Related to the preceding, but not resolved by the suggested resolution, is the following problem. Given:

    struct T {
	 operator int() const;
	 operator double() const;
    };

I believe the standard requires the following assignment to be ambiguous (even though I expect that would surprise the user):

    double x;
    void f(const T& t) { x = t; }

The problem is that both of these built-in operator=()s exist (13.6 [over.built] paragraph 18):

    double& operator=(double&, int);
    double& operator=(double&, double);

Both are an exact match on the first argument and a user conversion on the second. There is no rule that says one is a better match than the other.

The compilers that I have tried (even in their strictest setting) do not give a peep. I think they are not following the standard. They pick double& operator=(double&, double) and use T::operator double() const.

I hesitate to suggest changes to overload resolution, but a possible resolution might be to introduce a rule that, for built-in operator= only, also considers the conversion sequence from the second to the first type. This would also resolve the earlier question.

It would still leave x += t etc. ambiguous -- which might be the desired behavior and is the current behavior of some compilers.

Notes from the 04/01 meeting:

The difference between initialization and assignment is disturbing. On the other hand, promotion is ubiquitous in the language, and this is the beginning of a very slippery slope (as the second report above demonstrates).

Additional note (August, 2010):

See issue 507 for a similar example involving comparison operators.




205. Templates and static data members

Section: 14  [temp]     Status: drafting     Submitter: Mike Miller     Date: 11 Feb 2000

Static data members of template classes and of nested classes of template classes are not themselves templates but receive much the same treatment as template. For instance, 14 [temp] paragraph 1 says that templates are only "classes or functions" but implies that "a static data member of a class template or of a class nested within a class template" is defined using the template-declaration syntax.

There are many places in the clause, however, where static data members of one sort or another are overlooked. For instance, 14 [temp] paragraph 6 allows static data members of class templates to be declared with the export keyword. I would expect that static data members of (non-template) classes nested within class templates could also be exported, but they are not mentioned here.

Paragraph 8, however, overlooks static data members altogether and deals only with "templates" in defining the effect of the export keyword; there is no description of the semantics of defining a static data member of a template to be exported.

These are just two instances of a systematic problem. The entire clause needs to be examined to determine which statements about "templates" apply to static data members, and which statements about "static data members of class templates" also apply to static data members of non-template classes nested within class templates.

(The question also applies to member functions of template classes; see issue 217, where the phrase "non-template function" in 8.3.6 [dcl.fct.default] paragraph 4 is apparently intended not to include non-template member functions of template classes. See also issue 108, which would benefit from understanding nested classes of class templates as templates. Also, see issue 249, in which the usage of the phrase "member function template" is questioned.)

Notes from the 4/02 meeting:

Daveed Vandevoorde will propose appropriate terminology.




314. template in base class specifier

Section: 14.2  [temp.names]     Status: drafting     Submitter: Mark Mitchell     Date: 23 Aug 2001

The EDG front-end accepts:

template <typename T>
struct A {
  template <typename U>
  struct B {};
};

template <typename T>
struct C : public A<T>::template B<T> {
};

It rejects this code if the base-specifier is spelled A<T>::B<T>.

However, the grammar for a base-specifier does not allow the template keyword.

Suggested resolution:

It seems to me that a consistent approach to the solution that looks like it will be adopted for issue 180 (which deals with the typename keyword in similar contexts) would be to assume that B is a template if it is followed by a "<". After all, an expression cannot appear in this context.

Notes from the 4/02 meeting:

We agreed that template must be allowed in this context. The syntax needs to be changed. We also opened the related issue 343.

Additional note (August, 2010):

The same considerations apply to mem-initializer-ids, as noted in issue 1019.




1032. Empty pack expansions

Section: 14.5.3  [temp.variadic]     Status: drafting     Submitter: James Widman     Date: 2010-02-16

It was intended for empty pack expansions to be useful in contexts like base-specifiers, e.g.,

    template<class... T> struct A : T... {};
    A<> x; // ok?

However, the current wording provides no description of how that might work. (More generally, the problem arises in any context where the pack expansion follows a token that should only be present when the pack expansion is non-empty: following another argument in a function call, etc.)




996. Ambiguous partial specializations of member class templates

Section: 14.5.5  [temp.class.spec]     Status: drafting     Submitter: Doug Gregor     Date: 28 Oct, 2009

Given an example like

  template<typename T, typename U>
  struct Outer {
    template<typename X, typename Y> struct Inner;
    template<typename Y> struct Inner<T, Y> {};
    template<typename Y> struct Inner<U, Y> {};
  };
  Outer<int, int> outer;                      // #1
  Outer<int, int>::Inner<int, float> inner;   // #2

Is #1 ill-formed because of the identical partial specializations? If not, presumably #2 is ill-formed because of the resulting ambiguity (14.5.5.1 [temp.class.spec.match] paragraph 1).

Notes from the November, 2010 meeting:

The instantiation of Outer<int,int> results in duplicate declarations of the partial specialization, which are ill-formed by 9.2 [class.mem] paragraph 1. No normative change is required, but it might be helpful to add an example like this somewhere.




549. Non-deducible parameters in partial specializations

Section: 14.5.5.1  [temp.class.spec.match]     Status: drafting     Submitter: Martin Sebor     Date: 18 November 2005

In the following example, the template parameter in the partial specialization is non-deducible:

    template <class T> struct A { typedef T U; };
    template <class T> struct C { };
    template <class T> struct C<typename A<T>::U> { };

Several compilers issue errors for this case, but there appears to be nothing in the Standard that would make this ill-formed; it simply seems that the partial specialization will never be matched, so the primary template will be used for all specializations. Should it be ill-formed?

Notes from the April, 2006 meeting:

It was noted that there are similar issues for constructors and conversion operators with non-deducible parameters, and that they should probably be dealt with similarly.




560. Use of the typename keyword in return types

Section: 14.6  [temp.res]     Status: drafting     Submitter: Greg Comeau     Date: 11 February 2006

Consider the following example:

    template <class T> struct Outer {
        struct Inner {
            Inner* self();
        };
    };
    template <class T> Outer<T>::Inner*
        Outer<T>::Inner::self() { return this; }

According to 14.6 [temp.res] paragraph 3 (before the salient wording was inadvertently removed, see issue 559),

A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (14.6.2 [temp.dep]) but does not refer to a member of the current instantiation (14.6.2.1 [temp.dep.type]) shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming a typename-specifier.

Because Outer<T>::Inner is a member of the current instantiation, the Standard does not currently require that it be prefixed with typename when it is used in the return type of the definition of the self() member function. However, it is difficult to parse this definition correctly without knowing that the return type is, in fact, a type, which is what the typename keyword is for. Should the Standard be changed to require typename in such contexts?




1043. Qualified name lookup in the current instantiation

Section: 14.6.2.1  [temp.dep.type]