Date:  2017-02-06
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2014
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


C++ Standard Core Language Active Issues, Revision 97


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

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 WG21 N4606.

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:2014 document and corrected defects in the earlier 2011, 2003, and 1998 documents; others refer to text in the working draft for the next revision of the C++ language 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.

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:2014, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.

Information regarding C++ standardization can be found at http://isocpp.org/std.


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 a change in the working draft is required, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification.

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 between-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.

Accepted: Like a DR except that the issue concerns the wording of the current Working Paper rather than that of the current International Standard.

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.

C++11: A DR issue not resolved in CD2 but included in ISO/IEC 14882:2011.

CD3: A DR/DRWP or Accepted/WP issue not resolved in C++11 but included in the Committee Draft advanceed for balloting at the April, 2013 WG21 meeting.

C++14: A DR/DRWP or Accepted/WP issue not resolved in CD3 but included in ISO/IEC 14882:2014.

CD4: A DR/DRWP or Accepted/WP issue not resolved in C++14 but included in the Committee Draft advanced for balloting at the June, 2014 WG21 meeting.

DRWP: 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.

WP: An Accepted issue whose resolution is reflected in the current Working Paper.

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.

Concurrency: The issue deals with concurrency and is to be handled by the Concurrency and Parallelism Study Group (SG1) within WG21.


Issues with "Ready" Status


2218. Ambiguity and namespace aliases

Section: 3.4  [basic.lookup]     Status: ready     Submitter: Richard Smith     Date: 2015-12-29

There is implementation divergence on the status of the following example:

  namespace A { namespace B { int x; } }
  namespace C { namespace B = A::B; }
  using namespace A;
  using namespace C;
  int x = B::x;

This should presumably be valid: the lookup of B finds A::B and C::B, but it is not ambiguous because they denote the same entity. A similar example with a using-declaration or alias-declaration seems to be universally accepted. Perhaps the lookup rules need to be clarified regarding the status of this example.

Proposed resolution (November, 2016):

Change 3.4 [basic.lookup] paragraph 1 as follows:

The name lookup rules apply uniformly to all names (including typedef-names (7.1.3 [dcl.typedef]), namespace-names (7.3 [basic.namespace]), and class-names (9.1 [class.name])) wherever the grammar allows such names in the context discussed by a particular rule. Name lookup associates the use of a name with a declaration set of declarations (3.1 [basic.def]) of that name. Name lookup shall find an unambiguous declaration for the name (see 10.2 [class.member.lookup]). Name lookup may associate more than one declaration with a name if it finds the name to be a function name; The declarations found by name lookup shall either all declare the same entity or shall all declare functions; in the latter case, the declarations are said to form a set of overloaded functions (13.1 [over.load]). Overload resolution...



1677. Constant initialization via aggregate initialization

Section: 3.6.2  [basic.start.static]     Status: ready     Submitter: Daniel Krügler     Date: 2013-05-05

The resolution of issue 1489 added wording regarding value initialization to 3.6.2 [basic.start.static] paragraph 2 in an attempt to clarify the status of an example like

  int a[1000]{};

However, this example is aggregate initialization, not value initialization. Also, now that we allow brace-or-equal-initializers in aggregates, this wording also needs to be updated to allow an aggregate with constant non-static data member initializers to qualify for constant initialization.

Proposed resolution (November, 2017):

  1. Change 3.6.2 [basic.start.static] paragraph 2 as follows, converting the bulleted list into running text as indicated:

  2. A constant initializer for an a variable or temporary object o is an expression that initializer whose full-expression is a constant expression, except that it if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types. [Note: Such a class may have a non-trivial destructor —end note] Constant initialization is performed:

    If constant initialization is not performed...

  3. Change 5.20 [expr.const] paragraph 2 as follows:

    A conditional-expression An expression e is a core constant expression unless...



2205. Restrictions on use of alignas

Section: 7.6.1  [dcl.attr.grammar]     Status: ready     Submitter: Richard Smith     Date: 2015-11-30

According to 7.6.1 [dcl.attr.grammar] paragraph 5, a program is ill-formed if an attribute appertains to an entity or statement to which it is not allowed to apply. Presumably an alignment-specifier should have the same restriction.

Proposed resolution (November, 2016):

Change 7.6.1 [dcl.attr.grammar] paragraph 5 as follows:

Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears (Clause 6 [stmt.stmt], Clause 7 [dcl.dcl], Clause 8 [dcl.decl]). If an attribute-specifier-seq that appertains to some entity or statement contains an attribute or alignment-specifier that is not allowed to apply to that entity or statement, the program is ill-formed. If an attribute-specifier-seq appertains to a friend declaration (11.3 [class.friend]), that declaration shall be a definition. No attribute-specifier-seq shall appertain to an explicit instantiation (14.7.2 [temp.explicit]).



1622. Empty aggregate initializer for union

Section: 8.6.1  [dcl.init.aggr]     Status: ready     Submitter: Daveed Vandevoorde     Date: 2013-02-14

According to 8.6.1 [dcl.init.aggr] paragraph 15,

When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause for the first non-static data member of the union.

This would appear to preclude using {} as the initializer for a union, which would otherwise have reasonable semantics. Is there a reason for this restriction?

Also, paragraph 7 reads,

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.6.4 [dcl.init.list]).

There should presumably be special treatment for unions, so that only a single member is initialized in such cases.

(See also issue 1460.)

Proposed resolution (November, 2016):

Change 8.6.1 [dcl.init.aggr] paragraph 8 as follows:

If there are fewer initializer-clauses in the list than there are elements in the aggregate, then each Each non-variant element of the aggregate that is not explicitly initialized shall be is initialized from its default member initializer (9.2 [class.mem]) or, if there is no default member initializer, copy-initialized from an empty initializer list (8.6.4 [dcl.init.list]). If the aggregate is a union and the initializer list is empty, then

[Example:...




1860. What is a “direct member?”

Section: 9.3  [class.union]     Status: ready     Submitter: Dawn Perchik     Date: 2014-02-13

The term “direct member” is used in 9.3 [class.union] paragraph 8 but is not defined. It might be better to refer to the class's member-specification instead.

Additional note, October, 2015:

This issue is expected to be addressed by the wording of N4532 or a successor thereof (“Default Comparisons”).

Proposed resolution (November, 2016):

  1. Change 8.6.1 [dcl.init.aggr] paragraph 2 as follows:

  2. The elements of an aggregate are:

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

  4. The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere. A direct member of a class X is a member of X that was first declared within the member-specification of X, including anonymous union objects and direct members thereof. Members of a class are...
  5. Change 9.3.1 [class.union.anon] paragraph 1 as follows:

  6. A union of the form

    is called an anonymous union; it defines an unnamed type and an unnamed object of unnamed that type called an anonymous union object. Each member-declaration in the member-specification of an anonymous union

    ...



2174. Unclear rules for friend definitions in templates

Section: 14.5.4  [temp.friend]     Status: ready     Submitter: Richard Smith     Date: 2015-09-17

According to 14.5.4 [temp.friend] paragraph 4,

When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (3.2 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.

This seems to imply that:

  1. Instantiating a class template that contains a friend function definition instantiates the declaration, but not the definition, of that friend function, as usual (but see below).

  2. If the function is odr-used, a definition is instantiated for each such class template specialization whose template had a definition.

  3. If that results in multiple definitions, the program is ill-formed as usual.

The intent appears to be that the instantiated friend function declarations should be treated as if they were definitions, but that's not clear from the wording. This wording is also missing similar provisions for friend function template definitions; there is implementation divergence on the treatment of such cases.

There also does not appear to be wording that says that instantiating a class template specialization results in the instantiation of friend functions declared/defined therein (the relevant wording was removed from this section by issue 329). Presumably this should be covered in 14.7.1 [temp.inst] paragraph 1, which also includes the following wording that could be reused for the friend case:

However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

Also, the reliance on odr-use to trigger friend instantiation is out of date, as there are other contexts that can require an instantiation when there is no odr-use (a constexpr function invoked within an unevaluated operand).

Proposed resolution (October, 2015) [SUPERSEDED]:

  1. Delete 14.5.4 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (3.2 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 14.7.1 [temp.inst] paragraph 1 as follows:

  4. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 3.2 [basic.def.odr] and 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:...
  5. Change 14.7.1 [temp.inst] paragraph 3 as follows:

  6. Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.

Proposed resolution (November, 2016):

  1. Delete 14.5.4 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (3.2 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 14.7.1 [temp.inst] paragraph 1 as follows, splitting it into two paragraphs as indicated:

  4. ... [Note: Within a template declaration, a local class (9.4 [class.local]) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, exception-specifications, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. —end note]

    The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 3.2 [basic.def.odr] and 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:

      template<class T, class U>
      struct Outer {
        template<class X, class Y> struct Inner;
        template<class Y> struct Inner<T, Y>;     // #1a
        template<class Y> struct Inner<T, Y> { }; // #1b; OK: valid redeclaration of #1a
        template<class Y> struct Inner<U, Y> { }; // #2
      };
    
      Outer<int, int> outer; // error at #2
    

    Outer<int, int>::Inner<int, Y> is redeclared at #1b. (It is not defined but noted as being associated with a definition in Outer<T, U>.) #2 is also a redeclaration of #1a. It is noted as associated with a definition, so it is an invalid redeclaration of the same partial specialization.

      template<typename T> struct Friendly {
        template<typename U> friend int f(U) { return sizeof(T); }
      };
      Friendly<char> fc;
      Friendly<float> ff; // ill-formed: produces second definition of f(U)
    

    end example]

  5. Change 14.7.1 [temp.inst] paragraph 3 as follows:

    Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.



2191. Incorrect result for noexcept(typeid(v))

Section: 15.4  [except.spec]     Status: ready     Submitter: Mike Miller     Date: 2015-10-26

Consider the following example:

    struct B { virtual void f() { } };
    struct D : B { } d;
    bool b = noexcept(typeid(d));

According to 5.3.7 [expr.unary.noexcept] paragraph 3, the value of b should be false, because 15.4 [except.spec] bullet 14.6 says,

and d is such an expression. This is clearly bogus, as the expression cannot possibly throw; according to 5.2.8 [expr.typeid] paragraph 2, the condition under which the exception might be thrown is:

If the glvalue expression is obtained by applying the unary * operator to a pointer69 and the pointer is a null pointer value (4.11 [conv.ptr]), the typeid expression throws an exception (15.1 [except.throw]) of a type that would match a handler of type std::bad_typeid exception (18.7.4 [bad.typeid]).

Proposed resolution (November, 2016):

Change 15.4 [except.spec] bullet 13.6 as follows:

The set of potential exceptions of an expression e is empty if e is a core constant expression (5.20 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:






Issues with "Tentatively Ready" Status


2198. Linkage of enumerators

Section: 3.5  [basic.link]     Status: tentatively ready     Submitter: Richard Smith     Date: 2015-11-12

According to the rules in 3.5 [basic.link] paragraph 4, the enumerators of an enumeration type with linkage also have linkage. Having same-named enumerators in different translation units would seem to be innocuous. Is there a rationale for this rule?

Proposed resolution (January, 2017):

Delete 3.5 [basic.link] bullet 4.5:

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of




2214. Missing requirement on representation of integer values

Section: 3.9.1  [basic.fundamental]     Status: tentatively ready     Submitter: Richard Smith     Date: 2015-12-15

According to 3.9.1 [basic.fundamental] paragraph 3,

The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same.

The corresponding wording from C11 is,

The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same.

The C wording is arguably clearer, but it loses the implication of the C++ wording that the sign bit of a signed type is part of the value representation of the corresponding unsigned type.

Proposed resolution (January, 2017):

Change 3.9.1 [basic.fundamental] paragraph 3 as follows:

...The standard and extended unsigned integer types are collectively called unsigned integer types. The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, the representation of the same value in each of the two types is the same, and the value representation of each corresponding signed/unsigned type shall be the same. The standard signed integer types...



2201. Cv-qualification of array types

Section: 3.9.3  [basic.type.qualifier]     Status: tentatively ready     Submitter: Robert Haberlach     Date: 2015-11-15

Issue 1059 changed 3.9.3 [basic.type.qualifier] paragraph 6 to read,

An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements.

However, that change overlooked the earlier statement in paragraph 2,

A compound type (3.9.2 [basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4 [dcl.array]).

Proposed resolution (January, 2017):

Change 3.9.3 [basic.type.qualifier] paragraph 2 as follows:

A compound type (3.9.2 [basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4 [dcl.array]).



2206. Composite type of object and function pointers

Section: 5  [expr]     Status: tentatively ready     Submitter: Mike Miller     Date: 2015-12-01

Consider an example like

  void *p;
  void (*pf)();
  auto x = true ? p : pf;

The rules in 5 [expr] paragraph 13 say that the composite type between a void* and a function pointer type is void*. This is surprising, since a function pointer type cannot be implicitly converted to void*.

Proposed resolution (January, 2017):

Change 5 [expr] bullet 14.5 as follows:

The cv-combined type of two types T1 and T2 is a type T3 similar to T1 whose cv-qualification signature (4.5 [conv.qual]) is:




2224. Member subobjects and base-class casts

Section: 5.2.9  [expr.static.cast]     Status: tentatively ready     Submitter: Aaron Ballman     Date: 2016-01-13

The current wording of 5.2.9 [expr.static.cast] paragraph 2 appears to permit the following example:

  struct B {
    int i;
  };

  struct D : B {
    int j;
    B b;
  };

  int main() {
    D d;

    B &br = d.b;
    D &dr = static_cast<D&>(br);  // Okay?
  }

Presumably such casts should only be supported if the operand object is a base class subobject, not a member subobject.

Proposed resolution (January, 2017):

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

...If the object of type “cv1 B” is actually a base subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined. [Example:...



2220. Hiding index variable in range-based for

Section: 6.5.4  [stmt.ranged]     Status: tentatively ready     Submitter: Daveed Vandevoorde     Date: 2016-01-08

Given an example like

  void f() {
    int arr[] = { 1, 2, 3 };
    for (int val : arr) {
      int val;   // Redeclares index variable
    }
  }

one might expect that the redeclaration of the index variable would be an error, as it is in the corresponding classic for statement. However, the restriction that makes the latter an error is phrased in terms of the condition nonterminal in 6.4 [stmt.select] paragraph 3, and the range-based for does not refer to condition. Should there be an explicit prohibition of such a redeclaration?

Proposed resolution (January, 2017):

Add the following as a new paragraph before 6.5 [stmt.iter] paragraph 4:

If a name introduced in an init-statement or for-range-declaration is redeclared in the outermost block of the substatement, the program is ill-formed. [Example:

  void f() {
    for (int i = 0; i < 10; ++i)
      int i = 0;          // error: redeclaration
    for (int i : { 1, 2, 3 })
      int i = 1;          // error: redeclaration
  }

end example]

[Note: The requirements on conditions in iteration statements are described in 6.4 [stmt.select]. —end note]




2262. Attributes for asm-definition

Section: 7.4  [dcl.asm]     Status: tentatively ready     Submitter: Richard Smith     Date: 2016-05-04

There does not seem to be a good reason not to permit attributes on an asm declaration. This would be handy for things like:

  [[vendor::asm_syntax("intel")]] asm(...); 

Notes from the December, 2016 teleconference:

The omission seems to have been an oversight that should be corrected.

Proposed resolution (January, 2017):

Change 7.4 [dcl.asm] paragraph 1 as follows:

An asm declaration has the form

The asm declaration is conditionally-supported; its meaning is implementation-defined. The optional attribute-specifier-seq in an asm-definition appertains to the asm declaration. [Note: Typically it is used to pass information through the implementation to an assembler. —end note]




2259. Unclear context describing ambiguity

Section: 8.2  [dcl.ambig.res]     Status: tentatively ready     Submitter: Richard Smith     Date: 2016-04-12

According to 8.2 [dcl.ambig.res] paragraph 3,

Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses.

There are two problems here: first, a parameter-declaration-clause appears in a lambda-expression, not just in a function declaration. Second, the ambiguity can arise in a type-id appearing in any context, not just in a sizeof or typeid expression.

Proposed resolution (January, 2017):

Change 8.2 [dcl.ambig.res] paragraph 3 as follows:

Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id. [Example:...



2194. Impossible case in list initialization

Section: 13.3.1.7  [over.match.list]     Status: tentatively ready     Submitter: Robert Haberlach     Date: 2015-11-04

According to 13.3.1.7 [over.match.list] paragraph 1 says,

If the initializer list has no elements and T has a default constructor, the first phase is omitted.

However, this case cannot occur. If T is a non-aggregate class type with a default constructor and the initializer is an empty initializer list, the object will be value-constructed, per 8.6.4 [dcl.init.list] bullet 3.4. Overload resolution is only necessary if default-initialization (or a check of its semantic constraints) is implied, with the relevant section concerning candidates for overload resolution being 13.3.1.3 [over.match.ctor].

See also issue 1518.

Proposed resolution (January, 2017):

Change 13.3.1.7 [over.match.list] paragraph 1 as follows:

When objects of non-aggregate class type T are list-initialized such that 8.6.4 [dcl.init.list] specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:

If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen...






Issues with "Review" Status


1924. Definition of “literal” and kinds of literals

Section: 2.13  [lex.literal]     Status: review     Submitter: Saeed Amrollah Boyouki     Date: 2014-05-12

The term “literal” is used without definition except the implicit connection with the syntactic nonterminal literal. The relationships of English terms to syntactic nonterminals (such as “integer literal” and integer-literal) should be examined throughout 2.13 [lex.literal] and its subsections.

Notes from the November, 2016 meeting:

This issue will be handled editorially. It is being placed in "review" status until that point.




426. Identically-named variables, one internally and one externally linked, allowed?

Section: 3.5  [basic.link]     Status: review     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) [SUPERSEDED]:

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 from 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.

Proposed resolution (November, 2016):

Change 3.5 [basic.link] paragraph 6 as follows:

...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 program is ill-formed. [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: error, 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. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed.end example]




2248. Problems with sized delete

Section: 5.3.5  [expr.delete]     Status: review     Submitter: Richard Smith     Date: 2016-03-14

[Detailed description pending.]

Proposed resolution (December, 2016):

Change 5.3.5 [expr.delete] paragraph 11 as follows:

When a delete-expression is executed, the selected deallocation function shall be called with the address of the block of storage to be reclaimed most-derived object in the delete object case, or the address of the object suitably adjusted for the array allocation overhead (5.3.4 [expr.new]) in the delete array case, as its first argument. If a deallocation function with a parameter of type std::align_val_t is used, the alignment of the type of the object to be deleted is passed as the corresponding argument. If a deallocation function with a parameter of type std::size_t is used, the size of the block most-derived type, or of the array plus allocation overhead, respectively is passed as the corresponding argument. [Note: If this results in a call to a usual deallocation function, and either the first argument was not the result of a prior call to a usual allocation function or the second argument was not the corresponding argument in said call, the behavior is undefined (18.6.2.1 [new.delete.single], 18.6.2.2 [new.delete.array]). —end note]



2251. Unreachable enumeration list-initialization

Section: 8.6.4  [dcl.init.list]     Status: review     Submitter: Richard Smith     Date: 2016-03-22

[Detailed description pending.]

Proposed resolution (December, 2016):

Reorder the bullets in 8.6.4 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:




2183. Problems in description of potential exceptions

Section: 15.4  [except.spec]     Status: review     Submitter: Hubert Tong     Date: 2015-10-21

The description of the “set of potential exceptions of an expression” in 15.4 [except.spec] paragraph 15 does not appear to be fully recursive, so it can miss the effect of, e.g., a throw-expression as a subexpression. In addition, bullet 15.1.1, which reads,

omits the case where the postfix-expression is a function call whose return type is a function pointer with an exception specification.

Notes from the June, 2016 meeting:

This text will be replaced by the removal of dynamic exception specifications (P0003) and thus does not need to be changed at this time. The issue is placed in "review" status until document P0003 is adopted.






Issues with "Drafting" Status


536. Problems in the description of id-expressions

Section: _N4567_.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 _N4567_.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 _N4567_.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 _N4567_.5.1.1 [expr.prim.general].




1836. Use of class type being defined in trailing-return-type

Section: _N4567_.5.1.1  [expr.prim.general]     Status: drafting     Submitter: Mike Miller     Date: 2014-01-17

According to _N4567_.5.1.1 [expr.prim.general] paragraph 3,

Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (5.2.5 [expr.ref]) outside the member function body.

Is this special treatment of member access expressions intended to apply only to *this, or does it apply to other ways of specifying the class being defined in the object expression? For example,

  struct S {
    int i;
    auto f1() -> decltype((*this).i);      // okay
    auto f2(S& This) -> decltype(This.i);  // okay?
    auto f3() -> decltype(((S*)0)->i);     // okay?
  };

There is implementation divergence on this question.

If the intent is to allow object expressions other than *this to have the current class type, this specification should be moved from _N4567_.5.1.1 [expr.prim.general] to 5.2.5 [expr.ref] paragraph 2, which is where the general requirement for complete object expression types is found.

On a related point, the note immediately following the above-cited passage is not quite correct:

[Note: only class members declared prior to the declaration are visible. —end note]

This does not apply when the member is a “member of an unknown specialization,” per 14.6.2.1 [temp.dep.type] paragraph 5 bullet 3 sub-bullet 1; for example,

  template<typename T> struct S : T {
    auto f() -> decltype(this->x);
  };

Here x is presumed to be a member of the dependent base T and is not “declared prior to the declaration” that refers to it.




1837. Use of this in friend and local class declarations

Section: _N4567_.5.1.1  [expr.prim.general]     Status: drafting     Submitter: Hubert Tong     Date: 2014-01-13

The description of the use of this found in _N4567_.5.1.1 [expr.prim.general] paragraphs 3 and 4 allow it to appear in the declaration of a non-static member function following the optional cv-qualifier-seq and in the brace-or-equal-initializer of a non-static data member; all other uses are prohibited. These restrictions appear to allow questionable uses of this in several contexts. For example:

  template <typename T>
  struct Fish { static const bool value = true; };

  struct Other {
    int p();
    auto q() -> decltype(p()) *;
  };

  class Outer {
    // The following declares a member function of class Other.
    // Is this interpreted as Other* or Outer*?
    friend auto Other::q() -> decltype(this->p()) *;
    int g();
    int f() {
     extern void f(decltype(this->g()) *);
     struct Inner {
       // The following are all within the declaration of Outer::f().
       // Is this Outer* or Inner*?
       static_assert(Fish<decltype(this->g())>::value, "");
       enum { X = Fish<decltype(this->f())>::value };
       struct Inner2 : Fish<decltype(this->g())> { };
       friend void f(decltype(this->g()) *);
       friend auto Other::q() -> decltype(this->p()) *;
     };
     return 0;
    }
  };
  struct A {
   int f();
   bool b = [] {
    struct Local {
     static_assert(sizeof this->f() == sizeof(int), ""); // A or Local?
    };
   };
  };

There is implementation divergence on the treatment of these examples.




1938. Should hosted/freestanding be implementation-defined?

Section: 1.4  [intro.compliance]     Status: drafting     Submitter: Richard Smith     Date: 2014-06-09

Whether an implementation is hosted or freestanding is only required to be documented by the value of the __STDC_HOSTED__ macro (_N4567_.16.8 [cpp.predefined]). Should this characteristic be classified as implementation-defined, thus requiring documentation?




2151. Exception object is not created

Section: 1.8  [intro.object]     Status: drafting     Submitter: Hubert Tong     Date: 2015-06-26

According to 1.8 [intro.object] paragraph 1,

An object is created by a definition (3.1 [basic.def]), by a new-expression (5.3.4 [expr.new]) or by the implementation (12.2 [class.temporary]) when needed.

This should probably also include a throw-expression, with a cross-reference to 15.1 [except.throw].

Notes from the October, 2015 meeting:

This issue is expected to be resolved by the resolution of issue 1776.




1332. Handling of invalid universal-character-names

Section: 2.3  [lex.charset]     Status: drafting     Submitter: Mike Miller     Date: 2011-06-20

According to 2.3 [lex.charset] paragraph 2,

The character designated by the universal-character-name \UNNNNNNNN is that character whose character short name in ISO/IEC 10646 is NNNNNNNN; the character designated by the universal-character-name \uNNNN is that character whose character short name in ISO/IEC 10646 is 0000NNNN. If the hexadecimal value for a universal-character-name corresponds to a surrogate code point (in the range 0xD800-0xDFFF, inclusive), the program is ill-formed. Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed.

It is not specified what should happen if the hexadecimal value does not designate a Unicode code point: is that undefined behavior or does it make the program ill-formed?

As an aside, a note should be added explaining why these requirements apply to to an r-char-sequence when, as the footnote at the end of the paragraph explains,

A sequence of characters resembling a universal-character-name in an r-char-sequence (2.13.5 [lex.string]) does not form a universal-character-name.



369. Are new/delete identifiers or preprocessing-op-or-punc?

Section: 2.4  [lex.pptoken]     Status: drafting     Submitter: Martin v. Loewis     Date: 30 July 2002

2.4 [lex.pptoken] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 2.12 [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.)




1655. Line endings in raw string literals

Section: 2.4  [lex.pptoken]     Status: drafting     Submitter: Mike Miller     Date: 2013-04-26

According to 2.4 [lex.pptoken] paragraph 3,

If the input stream has been parsed into preprocessing tokens up to a given character:

However, phase 1 is defined as:

Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set (introducing new-line characters for end-of-line indicators) if necessary. The set of physical source file characters accepted is implementation-defined. Trigraph sequences (_N4140_.2.4 [lex.trigraph]) are replaced by corresponding single-character internal representations. Any source file character not in the basic source character set (2.3 [lex.charset]) is replaced by the universal-character-name that designates that character.

The reversion described in 2.4 [lex.pptoken] paragraph 3 specifically does not mention the replacement of physical end-of-line indicators with new-line characters. Is it intended that, for example, a CRLF in the source of a raw string literal is to be represented as a newline character or as the original characters?




1901. punctuator referenced but not defined

Section: 2.6  [lex.token]     Status: drafting     Submitter: Richard Smith     Date: 2014-03-25

The syntactic nonterminal punctuator appears in the grammar for token in 2.6 [lex.token], but it is nowhere defined. It should be merged with operator and given an appropriate list of tokens as a definition for the merged term.




189. Definition of operator and punctuator

Section: 2.12  [lex.operators]     Status: drafting     Submitter: Mike Miller     Date: 20 Dec 1999

The nonterminals operator and punctuator in 2.6 [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.12 [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.6 [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.12 [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.)




1656. Encoding of numerically-escaped characters

Section: 2.13.3  [lex.ccon]     Status: drafting     Submitter: Mike Miller     Date: 2013-04-30

According to 2.13.3 [lex.ccon] paragraph 4,

The escape \ooo consists of the backslash followed by one, two, or three octal digits that are taken to specify the value of the desired character. The escape \xhhh consists of the backslash followed by x followed by one or more hexadecimal digits that are taken to specify the value of the desired character. There is no limit to the number of digits in a hexadecimal sequence. A sequence of octal or hexadecimal digits is terminated by the first character that is not an octal digit or a hexadecimal digit, respectively. The value of a character literal is implementation-defined if it falls outside of the implementation-defined range defined for char (for literals with no prefix), char16_t (for literals prefixed by 'u'), char32_t (for literals prefixed by 'U'), or wchar_t (for literals prefixed by 'L').

It is not clearly stated whether the “desired character” being specified reflects the source or the target encoding. This particularly affects UTF-8 string literals (2.13.5 [lex.string] paragraph 7):

A string literal that begins with u8, such as u8"asdf", is a UTF-8 string literal and is initialized with the given characters as encoded in UTF-8.

For example, assuming the source encoding is Latin-1, is u8"\xff" supposed to specify a three-byte string whose first two bytes are 0xc3 0xbf (the UTF-8 encoding of \u00ff) or a two-byte string whose first byte has the value 0xff? (At least some current implementations assume the latter interpretation.)

Notes from the September, 2013 meeting:

The second interpretation (that the escape sequence specifies the execution-time code unit) is intended.




1859. UTF-16 in char16_t string literals

Section: 2.13.5  [lex.string]     Status: drafting     Submitter: Richard Smith     Date: 2014-02-12

The resolution of issue 1802 clarified that char16_t string literals can contain surrogate pairs, in contrast to char16_t character literals. However, there is no explicit requirement that char16_t literals be encoded as UTF-16, although that is explicitly stated for char16_t character literals, so it's not clear what the value is required to be in the surrogate-pair case.




1723. Multicharacter user-defined character literals

Section: 2.13.8  [lex.ext]     Status: drafting     Submitter: Mike Miller     Date: 2013-07-31

According to 2.13.3 [lex.ccon] paragraph 1, a multicharacter literal like 'ab' is conditionally-supported and has type int.

According to 2.13.8 [lex.ext] paragraph 6,

If L is a user-defined-character-literal, let ch be the literal without its ud-suffix. S shall contain a literal operator (13.5.8 [over.literal]) whose only parameter has the type of ch and the literal L is treated as a call of the form

A user-defined-character-literal like 'ab'_foo would thus require a literal operator

However, that is not one of the signatures permitted by 13.5.8 [over.literal] paragraph 3.

Should multicharacter user-defined-character-literals be conditionally-supported? If so, 13.5.8 [over.literal] paragraph 3 should be adjusted accordingly. If not, a note in 2.13.8 [lex.ext] paragraph 6 saying explicitly that they are not supported would be helpful.




1735. Out-of-range literals in user-defined-literals

Section: 2.13.8  [lex.ext]     Status: drafting     Submitter: Mike Miller     Date: 2013-08-12

The description of the numeric literals occurring as part of user-defined-integer-literals and user-defined-floating-literals in 2.13.8 [lex.ext] says nothing about whether they are required to satisfy the same constraints as literals that are not part of a user-defined-literal. In particular, because it is the spelling, not the value, of the literal that is used for raw literal operators and literal operator templates, there is no particular reason that they should be restricted to the maximum values and precisions that apply to ordinary literals (and one could imagine that this would be a good notation for allowing literals of extended-precision types).

Is this relaxation of limits intended to be required, or is it a quality-of-implementation issue? Should something be said, either normatively or non-normatively, about this question?




1529. Nomenclature for variable vs reference non-static data member

Section: 3  [basic]     Status: drafting     Submitter: Daniel Krügler     Date: 2012-07-24

According to 3 [basic] paragraph 6,

A variable is introduced by the declaration of a reference other than a non-static data member or of an object.

In other words, non-static data members of reference type are not variables. This complicates the wording in a number of places, where the text refers to “variable or data member,” presumably to cover the reference case, but that phrasing could lead to the mistaken impression that all data members are not variables. It would be better if either there were a term for the current phrase “variable or data member” or if there were a less-unwieldy term for “non-static data member of reference type” that could be used in place of “data member” in the current phrasing.




1581. When are constexpr member functions defined?

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Richard Smith     Date: 2012-10-29

Clause 12 [special] is perfectly clear that special member functions are only implicitly defined when they are odr-used. This creates a problem for constant expressions in unevaluated contexts:

   struct duration {
     constexpr duration() {}
     constexpr operator int() const { return 0; }
   };
   // duration d = duration(); // #1
   int n = sizeof(short{duration(duration())});

The issue here is that we are not permitted to implicitly define constexpr duration::duration(duration&&) in this program, so the expression in the initializer list is not a constant expression (because it invokes a constexpr function which has not been defined), so the braced initializer contains a narrowing conversion, so the program is ill-formed.

If we uncomment line #1, the move constructor is implicitly defined and the program is valid. This spooky action at a distance is extremely unfortunate. Implementations diverge on this point.

There are also similar problems with implicit instantiation of constexpr functions. It is not clear which contexts require their instantiation. For example:

  template<int N> struct U {};
  int g(int);
  template<typename T> constexpr int h(T) { return T::error; }
  template<typename T> auto f(T t) -> U<g(T()) + h(T())> {}
  int f(...);
  int k = f(0);

There are at least two different ways of modeling the current rules:

These two approaches can be distinguished by code like this:

  int k = sizeof(U<0 && h(0)>);

Under the first approach, this code is valid; under the second, it is ill-formed.

A possible approach to resolving this issue would be to change the definition of “potentially-evaluated” such that template arguments, array bounds, and braced-init-lists (and any other expressions which are constant evaluated) are always potentially-evaluated, even if they appear within an unevaluated context, and to change 14.7.1 [temp.inst] paragraph 3 to say simply that function template specializations are implicitly instantiated when they are odr-used.

A related question is whether putatively constexpr constructors must be instantiated in order to determine whether their class is a literal type or not. See issue 1358.

Jason Merrill:

I'm concerned about unintended side-effects of such a large change to “potentially-evaluated;” I would prefer something that only affects constexpr.

It occurs to me that this is parallel to issue 1330: just like we want to instantiate exception specifiers for calls in unevaluated context, we also want to instantiate constexpr functions. I think we should define some other term to say when there's a reference to a declaration, and then say that the declaration is odr-used when that happens in potentially-evaluated context.

Notes from the April, 2013 meeting:

An additional question was raised about whether constexpr functions should be instantiated as a result of appearing within unevaluated subexpressions of constant expressions. For example:

  #include <type_traits>

  template <class T> constexpr T f(T t) { return +t; }

  struct A { };

  template <class T>
  decltype(std::is_scalar<T>::value ? T::fail : f(T()))
    g() { }

  template <class T>
  void g(...);

  int main()
  {
    g<A>();
  }

If constexpr instantiation happens during constant expression evaluation, f<A> is never instantiated and the program is well-formed. If constexpr instantiation happens during parsing, f<A> is instantiated and the program is ill-formed.




1849. Variable templates and the ODR

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Richard Smith     Date: 2014-02-03

The description in 3.2 [basic.def.odr] paragraph 6 of when entities can be multiply-declared in a program does not, but should, discuss variable templates.




1897. ODR vs alternative tokens

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Hubert Tong     Date: 2014-03-21

According to 2.5 [lex.digraph] paragraph 2,

In all respects of the language, each alternative token behaves the same, respectively, as its primary token, except for its spelling.

However, the primary and alternative tokens are different tokens, which runs afoul of the ODR requirement in 3.2 [basic.def.odr] paragraph 6 that the definitions consist of the “same sequence of tokens.” This wording should be amended to allow for use of primary and alternative tokens.




2020. Inadequate description of odr-use of implicitly-invoked functions

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Richard Smith     Date: 2014-10-08

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

A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions (3.4 [basic.lookup], 13.3 [over.match], 13.4 [over.over]), unless it is a pure virtual function and its name is not explicitly qualified. [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.6 [dcl.init]). A constructor selected to copy or move an object of class type is odr-used even if the call is actually elided by the implementation (12.8 [class.copy]). —end note] An allocation or deallocation function for a class is odr-used by a new expression appearing in a potentially-evaluated expression as specified in 5.3.4 [expr.new] and 12.5 [class.free]. A deallocation function for a class is odr-used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5 [expr.delete] and 12.5 [class.free].

There are a couple of problems with this specification. First, contrary to the note, the names of overloaded operators, conversion functions, etc., do not appear in potentially-evaluated expressions, so the normative text does not make the note true. Also, the “as specified in” references do not cover odr-use explicitly, only the invocation of the functions.

One possible way of addressing these deficiencies would be a blanket rule like,

A function is odr-used if it is invoked by a potentially-evaluated expression.

(The existing wording about appearing in a potentially-evaluated expression would still be needed for non-call references.)




2083. Incorrect cases of odr-use

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Hubert Tong     Date: 2015-02-11

The resolution of issue 1741 was not intended to cause odr-use to occur in cases where it did not do so previously. However, in an example like

  extern int globx;
  int main() {
    const int &x = globx;
    struct A {
     const int *foo() {
       return &x;
     }
    } a;
    return *a.foo();
  }

x satisfies the requirements for appearing in a constant expression, but applying the lvalue-to-rvalue converstion to x does not yield a constant expression. Similarly,

  struct A {
    int q;
    constexpr A(int q) : q(q) { }
    constexpr A(const A &a) : q(a.q * 2) { }
  };

  int main(void) {
    constexpr A a(42);
    constexpr int aq = a.q;
    struct Q {
     int foo() { return a.q; }
    } q;
    return q.foo();
  }

a satisfies the requirements for appearing in a constant expression, but applying the lvalue-to-rvalue conversion to a invokes a non-trivial function.




2103. Lvalue-to-rvalue conversion is irrelevant in odr-use of a reference

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Richard Smith     Date: 2015-03-17

Issue 1741 accidentally caused 3.2 [basic.def.odr] to indicate that an lvalue-to-rvalue conversion is necessary for the odr-use of a reference. This is incorrect; any appearance of the reference name in a potentially-evaluated expression should require the reference to be defined.

See also issue 2083.




2170. Unclear definition of odr-use for arrays

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Hubert Tong     Date: 2015-09-02

The current definition of odr-use of a variable is problematic when applied to an array:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1 [conv.lval]) to x yields a constant expression (5.20 [expr.const]) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1 [conv.lval]) is applied to e, or e is a discarded-value expression (Clause 5 [expr]).

Consider an example like

  struct S {
    constexpr static const int arr[3] = { 0, 1, 2 };
  };
  int i = S::arr[1];  // Should not require S::arr to be defined

Although the “set of potential results” test correctly handles the subscripting operation (since the resolution of issue 1926), it requires applying the lvalue-to-rvalue conversion to S::arr itself and not just to the result of the subscripting operation. Class objects exhibit a similar problem.




2242. ODR violation with constant initialization possibly omitted

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Hubert Tong     Date: 2016-03-05

Consider the following example:

  // tu1.cpp
  extern const int a = 1;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }

  // tu2.cpp
  extern const int a;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }
  int main() {
    return *decltype(f())()();
  }

Here, b may or may not have constant initialization. This example should be an ODR violation.

(Split off from issue 2123.)




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

Section: 3.3  [basic.scope]     Status: drafting     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.block]), the presence of a friend specifier (11.3 [class.friend]), certain uses of the elaborated-type-specifier (7.1.7.3 [dcl.type.elab]), and using-directives (7.3.4 [namespace.udir]) alter this general behavior.
  5. Change 3.3.3 [basic.scope.block] 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.4 [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.]

Notes from the October, 2015 meeting:

This issue has been returned to "drafting" status to be reconciled with changes to the underlying existing text.




2165. Namespaces, declarative regions, and translation units

Section: 3.3.1  [basic.scope.declarative]     Status: drafting     Submitter: Richard Smith     Date: 2015-07-30

The definition of “declarative region” given in 3.3.1 [basic.scope.declarative] paragraph 1 is,

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.

According to 7.3 [basic.namespace] paragraph 1,

Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units.

This seems like a misuse of the term “declarative region”; in particular, a name x declared in namespace N in translation unit A cannot be used as an unqualified name in the part of namespace N in translation unit B unless it is also declared in B. See also issue 1884.




2164. Name hiding and using-directives

Section: 3.3.10  [basic.scope.hiding]     Status: drafting     Submitter: Richard Smith     Date: 2015-07-26

Consider the following example:

   const int i = -1;
   namespace T {
     namespace N { const int i = 1; }
     namespace M {
       using namespace N;
       int a[i];
     }
   }

According to 3.4.1 [basic.lookup.unqual], lookup for i finds T::N::i and stops. However, according to 3.3.10 [basic.scope.hiding] paragraph 1, the appearance of T::N::i in namespace T does not hide ::i, so both declarations of i are visible in the declaration of a.

It seems strange that we specify this name hiding rule in two different ways in two different places, but they should at least be consistent.

On a related note, the wording in 3.4.1 [basic.lookup.unqual] paragraph 2, “as if they were declared in the nearest enclosing namespace...” could be confusing with regard to the “declared in the same scope” provisions of 3.3.10 [basic.scope.hiding].




555. Pseudo-destructor name lookup

Section: 3.4  [basic.lookup]     Status: drafting     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.

Notes from the August, 2011 meeting:

The proposed resolution must be updated with respect to the current wording of the WP.




192. Name lookup in parameters

Section: 3.4.1  [basic.lookup.unqual]     Status: drafting     Submitter: Alan Nash     Date: 6 Jan 2000

The description of name lookup in the parameter-declaration-clause of member functions in 3.4.1 [basic.lookup.unqual] paragraphs 7-8 is flawed in at least two regards.

First, both paragraphs 7 and 8 apply to the parameter-declaration-clause of a member function definition and give different rules for the lookup. Paragraph 7 applies to names "used in the definition of a class X outside of a member function body...," which includes the parameter-declaration-clause of a member function definition, while paragraph 8 applies to names following the function's declarator-id (see the proposed resolution of issue 41), including the parameter-declaration-clause.

Second, paragraph 8 appears to apply to the type names used in the parameter-declaration-clause of a member function defined inside the class definition. That is, it appears to allow the following code, which was not the intent of the Committee:

    struct S {
        void f(I i) { }
        typedef int I;
    };

Additional note, January, 2012:

brace-or-equal-initializers for non-static data members are intended effectively as syntactic sugar for mem-initializers in constructor definitions; the lookup should be the same.




1906. Name lookup in member friend declaration

Section: 3.4.1  [basic.lookup.unqual]     Status: drafting     Submitter: Richard Smith     Date: 2014-03-30

According to 3.4.1 [basic.lookup.unqual] paragraph 10,

In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in the declarator-id is first looked up in the scope of the member function's class (10.2 [class.member.lookup]). If it is not found, or if the name is part of a template-argument in the declarator-id, the look up is as described for unqualified names in the definition of the class granting friendship.

The corresponding specification for non-friend declarations in paragraph 8 applies the class-scope lookup only to names that follow the declarator-id. The same should be true in friend declarations.




1828. nested-name-specifier ambiguity

Section: 3.4.3  [basic.lookup.qual]     Status: drafting     Submitter: Richard Smith     Date: 2014-01-08

Issue 125 concerned an example like

  friend A::B::C();

which might be parsed as either

  friend A (::B::C)();

or

  friend A::B (::C)();

Its resolution attempted to make such constructs unambiguously ill-formed by allowing any identifier, not just namespaces and types, to appear in a nested-name-specifier, apparently on the assumption that C in this case would become part of an ill-formed nested-name-specifier instead of being taken as the unqualified-id in a qualified-id. Unfortunately, the current specification does not implement that intent, leaving both parses as valid possibilities.

A different approach might be to adjust the specification of the lookup of names appearing in nested-name-specifiers from

If a :: scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that :: considers only namespaces, types, and templates whose specializations are types. If the name found does not designate a namespace or a class, enumeration, or dependent type, the program is ill-formed.

to

Lookup of an identifier followed by a :: scope resolution operator considers only namespaces, types, and templates whose specializations are types. If an identifer, template-id, or decltype-specifier is followed by a :: scope resolution operator, the name shall designate a namespace, class, enumeration, or dependent type, and shall form part of a nested-name-specifier.

This approach would also remove the need for deferred lookup for template-ids and thus resolve issue 1771.




2070. using-declaration with dependent nested-name-specifier

Section: 3.4.3.1  [class.qual]     Status: drafting     Submitter: Richard Smith     Date: 2015-01-15

It makes no sense for a user to write a class template that contains a using-declaration that is sometimes an inheriting constructor declaration and sometimes pulls in a named value from a base class; These are sufficiently different things that we're doing them a disservice by conflating them. We're also doing a disservice to all readers of the code, by allowing an inheriting constructor to be written using a syntax that does not look like one.

In an inheriting constructor using-declaration, the nested-name-specifier and the unqualified-id should be required to be the same identifier.

Notes from the May, 2015 meeting:

The consensus of CWG was that the same name should be required when the nested-name-specifier is dependent and in the using-declaration case but should be allowed to be different in all other cases. See also issues 156 and 399.




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.




1291. Looking up a conversion-type-id

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2011-04-10

The Standard talks about looking up a conversion-type-id as if it were an identifier (3.4.5 [basic.lookup.classref] paragraph 7), but that is not exactly accurate. Presumably it should talk instead about looking up names (if any) appearing in the type-specifier-seq of the conversion-type-id.




1835. Dependent member lookup before <

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Richard Smith     Date: 2014-01-17

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.

Given

   template<typename T> T end(T);
   template<typename T>
   bool Foo(T it) {
     return it->end < it->end;
   }

since it is dependent and thus end cannot be looked up in the class of the object expression, it is looked up in the context of the postfix-expression. This lookup finds the function template, making the expression ill-formed.

One possibility might be to limit the lookup to the class of the object expression when the object expression is dependent.




1908. Dual destructor lookup and template-ids

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Hubert Tong     Date: 2014-03-31

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

If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. If the type T of the object expression is of a class type C, the type-name is also looked up in the scope of class C. At least one of the lookups shall find a name that refers to (possibly cv-qualified) T.

This would apply to an example like

  namespace K {
    template <typename T, typename U = char> struct A { };
    A<short> *a;
  }

  template <typename T> using A = K::A<short, T>;

  int main() {
    K::a->~A<char>();
  }

Current implementations, however, only apply the dual lookup when the type-name is not a template-id. The specification should be changed to reflect current practice.




1839. Lookup of block-scope extern declarations

Section: 3.5  [basic.link]     Status: drafting     Submitter: Hubert Tong     Date: 2014-01-18

According to 3.5 [basic.link] paragraph 6,

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed.

It is not clear how declarations that are in the lexical scope of the block-scope declaration but not members of the nearest enclosing namespace (see 7.3.1 [namespace.def] paragraph 6) should be treated. (For example, the definition of the function in which the block extern appears might be defined in an enclosing namespace, with a visible declaration of the name in that namespace, or it might be a member function of a class containing a member function of the name being declared.) Should such declarations be produce an error or should the lexically-nearer declaration simply be ignored? There is implementation divergence on this point.




1884. Unclear requirements for same-named external-linkage entities

Section: 3.5  [basic.link]     Status: drafting     Submitter: Richard Smith     Date: 2014-02-27

According to 3.5 [basic.link] paragraph 9,

Two names that are the same (Clause 3 [basic]) and that are declared in different scopes shall denote the same variable, function, type, enumerator, template or namespace if

This is not as clear as it should be. The intent is that this rule prevents declaring a name with extenal linkage to be, for instance, a type in one translation unit and a namespace in a different translation unit. It has instead been read as begging the question of what it means for two entities to be the same. The wording should be tweaked to make the intention clear. Among other things, it should be clarified that "declared in" refers to the namespace of which the name is a member, not the lexical scope in which the declaration appears (which affects friend declarations, block-scope extern declarations, and elaborated-type-specifiers).

There is a similar restriction in 3.3.1 [basic.scope.declarative] paragraph 4 dealing with declarations within a single declarative region, while 3.5 [basic.link] paragraph 9 deals with names that are associated via linkage. The relationship between these complementary requirements may need to be clarified as well.

See also issue 2165.




2058. More errors from internal-linkage namespaces

Section: 3.5  [basic.link]     Status: drafting     Submitter: Richard Smith     Date: 2014-12-15

Issue 1603 dealt with omissions in the application of the change to give unnamed namespaces internal linkage, but its resolution overlooked a couple of items. According to 3.5 [basic.link] paragraph 6,

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage.

The last sentence should say, “...receives the linkage of the innermost enclosing namespace.”

Also, 3.5 [basic.link] paragraph 8 says,

A type without linkage shall not be used as the type of a variable or function with external linkage unless

This bullet cannot occur, since a function or variable declared within an unnamed namespace cannot have external linkage.




1294. Side effects in dynamic/static initialization

Section: 3.6.2  [basic.start.static]     Status: drafting     Submitter: Daniel Krügler     Date: 2011-04-08

According to 3.6.2 [basic.start.static] paragraph 3,

An implementation is permitted to perform the initialization of a non-local variable with static storage duration as a static initialization even if such initialization is not required to be done statically, provided that

This does not consider side effects of the initialization in this determination, only the values of namespace-scope variables.




1986. odr-use and delayed initialization

Section: 3.6.2  [basic.start.static]     Status: drafting     Submitter: Richard Smith     Date: 2014-08-21

The current wording of 3.6.2 [basic.start.static] allows deferral of static and thread_local initialization until a variable or function in the containing translation unit is odr-used. This requires implementations to avoid optimizing away the relevant odr-uses. We should consider relaxing the rule to allow for such optimizations.

Proposed resolution (November, 2014):

For a variable V with thread or static storage duration, let X be the set of all variables with the same storage duration as V that are defined in the same translation unit as V. If the observable behavior of the abstract machine (1.8 [intro.object]) depends on the value of V through an evaluation E, and E is not sequenced before the end of the initialization of any variable in X, then the end of the initialization of all variables in X is sequenced before E.

There is also a problem (submitted by David Majnemer) if the odr-use occurs in a constexpr context that does not require the variable to be constructed. For example,

  struct A { A(); };
  thread_local A a;

  constexpr bool f() { return &a != nullptr; }

It doesn't seem possible to construct a before its odr-use in f.

There is implementation divergence in the handling of this example.

Notes from the November, 2014 meeting:

CWG determined that the second part of the issue (involving constexpr) is not a defect because the address of an object with thread storage duration is not a constant expression.

Additional note, May, 2015:

CWG failed to indicate where and how to apply the wording in the proposed resolution. In addition, further review has raised concern that “sequenced before” may be the wrong relation to use for the static storage duration case because it implies “in the same thread.”

Notes from the October, 2015 meeting:

The suggested wording is intended to replace some existing wording in 3.6.2 [basic.start.static] paragraph 2. CWG affirmed that the correct relationship is “happens before” and not “sequenced before.”




2148. Thread storage duration and order of initialization

Section: 3.6.2  [basic.start.static]     Status: drafting     Submitter: Hubert Tong     Date: 2015-06-22

The terms “ordered” and “unordered” initialization are only defined in 3.6.2 [basic.start.static] paragraph 2 for entities with static storage duration. They should presumably apply to entities with thread storage duration as well.




1634. Temporary storage duration

Section: 3.7  [basic.stc]     Status: drafting     Submitter: Richard Smith     Date: 2013-03-04

According to 3.7 [basic.stc] paragraph 2,

Static, thread, and automatic storage durations are associated with objects introduced by declarations (3.1 [basic.def]) and implicitly created by the implementation (12.2 [class.temporary]).

The apparent intent of the reference to 12.2 [class.temporary] is that a temporary whose lifetime is extended to be that of a reference with one of those storage durations is considered also to have that storage duration. This interpretation is buttressed by use of the phrase “an object with the same storage duration as the temporary” (twice) in 12.2 [class.temporary] paragraph 5.

There are two problems, however: first, the specification of lifetime extension of temporaries (also in 12.2 [class.temporary] paragraph 5) does not say anything about storage duration. Also, nothing is said in either of these locations about the storage duration of a temporary whose lifetime is not extended.

The latter point is important because 3.8 [basic.life] makes a distinction between the lifetime of an object and the acquisition and release of the storage the object occupies, at least for objects with non-trivial initialization and/or a non-trivial destructor. The assumption is made in 12.2 [class.temporary] and elsewhere that the storage in which a temporary is created is no longer available for reuse, as specified in 3.8 [basic.life], after the lifetime of the temporary has ended, but this assumption is not explicitly stated. One way to make that assumption explicit would be to define a storage duration for temporaries whose lifetime is not extended.

See also issue 2256.




1676. auto return type for allocation and deallocation functions

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: drafting     Submitter: Richard Smith     Date: 2013-05-04

Do we need explicit language to forbid auto as the return type of allocation and deallocation functions?

(See also issue 1669.)




1910. “Shall” requirement applied to runtime behavior

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: drafting     Submitter: Richard Smith     Date: 2014-04-12

According to 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 3,

If an allocation function declared with a non-throwing exception-specification (15.4 [except.spec]) fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception (15.1 [except.throw]) of a type that would match a handler (15.3 [except.handle]) of type std::bad_alloc (18.6.3.1 [bad.alloc]).

The use of the word “shall” to constrain runtime behavior is inappropriate, as it normally identifies cases requiring a compile-time diagnostic.




2073. Allocating memory for exception objects

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: drafting     Submitter: Jonathan Wakely     Date: 2015-01-20

According to 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 4,

[Note: In particular, a global allocation function is not called to allocate storage for objects with static storage duration (3.7.1 [basic.stc.static]), for objects or references with thread storage duration (3.7.2 [basic.stc.thread]), for objects of type std::type_info (5.2.8 [expr.typeid]), or for an exception object (15.1 [except.throw]). —end note]

The restriction against allocating exception objects on the heap was intended to ensure that heap exhaustion could be reported by throwing an exception, i.e., that obtaining storage for std::bad_alloc could not fail because the heap was full. However, this implicitly relied on the assumption of a single thread and does not scale to large numbers of threads, so the restriction should be lifted and another mechanism found for guaranteeing the ability to throw std::bad_alloc.

Notes from the February, 2016 meeting:

The prohibition of using an allocation function appears only in a note, although there is a normative reference to the rule in 15.1 [except.throw] paragraph 4. CWG was in favor of retaining the prohibition of using a C++ allocation function for the memory of an exception object, with the implicit understanding that use of malloc would be permitted. The resolution for this issue should delete the note and move the prohibition to normative text in the relevant sections.




2042. Exceptions and deallocation functions

Section: 3.7.4.2  [basic.stc.dynamic.deallocation]     Status: drafting     Submitter: Richard Smith     Date: 2014-11-13

According to 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3,

If a deallocation function terminates by throwing an exception, the behavior is undefined.

This seems to be in conflict with the provisions of 15.4 [except.spec]: if a deallocation function throws an exception that is not allowed by its exception-specification, 15.4 [except.spec] paragraph 10 would appear to give the program defined behavior (calling std::unexpected() or std::terminate()). (Note that 15.4 [except.spec] paragraph 18 explicitly allows an explicit exception-specification for a deallocation function.)




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.

(See also issues 1116 and 1338.)

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1116.




1530. Member access in out-of-lifetime objects

Section: 3.8  [basic.life]     Status: drafting     Submitter: Howard Hinnant     Date: 2012-07-26

According to 3.8 [basic.life] paragraphs 5 and 6, a program has undefined behavior if a pointer or glvalue designating an out-of-lifetime object

is used to access a non-static data member or call a non-static member function of the object

It is not clear what the word “access” means in this context. A reasonable interpretation might be using the pointer or glvalue as the left operand of a class member access expression; alternatively, it might mean to read or write the value of that member, allowing a class member access expression that is used only to form an address or bind a reference.

This needs to be clarified. A relevant consideration is the recent adoption of the resolution of issue 597, which eased the former restriction on simple address manipulations involving out-of-lifetime objects: if base-class offset calculations are now allowed, why not non-static data member offset calculations?

(See also issue 1531 for other uses of the term “access.”)

Additional note (January, 2013):

A related question is the meaning of the phrase “before the constructor begins execution” in 12.7 [class.cdtor] paragraph 1 means:

For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.

For example:

  struct DerivedMember { ... };

  struct Base {
    Base(DerivedMember const&);
  };

  struct Derived : Base {
    DerivedMember x;
    Derived() : Base(x) {}
  };

  Derived a;

Is the reference to Derived::x in the mem-initializer valid?

Additional note (March, 2013):

This clause is phrased in terms of the execution of the constructor. However, it is possible for an aggregate to have a non-trivial default constructor and be initialized without executing a constructor. The wording needs to be updated to allow for non-constructor initialization to avoid appearing to imply undefined behavior for an example like:

  struct X {
    std::string s;
  } x = {};
  std::string t = x.s;  // No constructor called for x: undefined behavior?



1853. Defining “allocated storage”

Section: 3.8  [basic.life]     Status: drafting     Submitter: Jeffrey Yasskin     Date: 2014-02-09

The term “allocated storage” is used in several places in the Standard to refer to memory in which an object may be created (dynamic, static, or automatic storage), but it has no formal definition.




1701. Array vs sequence in object representation

Section: 3.9  [basic.types]     Status: drafting     Submitter: Lawrence Crowl     Date: 2013-06-14

According to 3.9 [basic.types] paragraph 4,

The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).

However, it is not clear that a “sequence” can be indexed, as an array can and as is required for the implementation of memcpy and similar code.

Additional note, November, 2014:

An additional point of concern has been raised as to whether it is appropriate to refer to the constituent bytes of an object as being “objects” themselves, along with the interaction of this specification with copying or not copying parts of the object representation that do not participate in the value representation of the object (“padding” bytes).




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.




2051. Simplifying alias rules

Section: 3.10  [basic.lval]     Status: drafting     Submitter: Richard Smith     Date: 2014-12-03

The aliasing rules of 3.10 [basic.lval] paragraph 10 were adapted from C with additions for C++. However, a number of the points either do not apply or are subsumed by other points. For example, the provision for aggregate and union types is needed in C for struct assignment, which in C++ is done via constructors and assignment operators in C++, not by accessing the complete object.

Suggested resolution:

Replace 3.10 [basic.lval] paragraph 10 as follows:

If a program attempts to access the stored value of an object through a glvalue whose type is not similar (4.5 [conv.qual]) to one of the following types the behavior is undefined: [Footnote:... —end footnote]

Additional note, October, 2015:

It has been suggested that the aliasing rules should be extended to permit an object of an enumeration with a fixed underlying type to alias an object with that underlying 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.12  [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.12 [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.)




1249. Cv-qualification of nested lambda capture

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: James Widman     Date: 2011-03-02

Consider the following example:

    void f(int i) {
      auto l1 = [i] {
        auto l2 = [&i] {
          ++i;    // Well-formed?
        };
      };
    }

Because the l1 lambda is not marked as mutable, its operator() is const; however, it is not clear from the wording of 5.1.5 [expr.prim.lambda] paragraph 16 whether the captured member of the enclosing lambda is considered const or not.




1468. typeid, overload resolution, and implicit lambda capture

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Doug Gregor     Date: 2012-02-08

According to 5.1.5 [expr.prim.lambda] paragraph 11, a variable is implicitly captured if it is odr-used. In the following example,

  struct P {
   virtual ~P();
  };

  P &f(int&);
  int f(const int&);

  void g(int x) {
   [=] {
    typeid(f(x));
   };
  }

x is only odr-used if the operand of typeid is a polymorphic lvalue; otherwise, the operand is unevaluated (5.2.8 [expr.typeid] paragraphs 2-3). Whether the operand is a polymorphic lvalue depends on overload resolution in this case, which depends on whether x is captured or not: if x is captured, since the lambda is not mutable, the type of x in the body of the lambda is const int, while if it is not captured, it is just int. However, the const int version of f returns int and the int version of f returns a polymorphic lvalue, leading to a conundrum: x is only captured if it is not captured, and vice versa.

Notes from the October, 2012 meeting:

The approach favored by CWG was to specify that the operand of typeid is considered to be odr-used for the purpose of determining capture.




1913. decltype((x)) in lambda-expressions

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Dinka Ranns     Date: 2014-04-15

According to 5.1.5 [expr.prim.lambda] paragraph 19,

Every occurrence of decltype((x)) where x is a possibly parenthesized id-expression that names an entity of automatic storage duration is treated as if x were transformed into an access to a corresponding data member of the closure type that would have been declared if x were an odr-use of the denoted entity.

This formulation is problematic because it assumes that x can be captured and, if captured, would result in a member of the closure class. The former is not true if the lambda has no capture-default, and the latter is not guaranteed if the capture-default is &.




1937. Incomplete specification of function pointer from lambda

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Richard Smith     Date: 2014-06-05

According to 5.1.5 [expr.prim.lambda] paragraph 6,

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C ++ language linkage (7.5 [dcl.link]) having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.

This does not mention the object for which the function call operator would be invoked (although since there is no capture, presumably the function call operator makes no use of the object pointer). This could be addressed by relating the behavior of the function call operator to a notional temporary, or the function call operator for such closure classes could be made static.




1973. Which parameter-declaration-clause in a lambda-expression?

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Dinka Ranns     Date: 2014-07-16

According to 5.1.5 [expr.prim.lambda] paragraph 5,

The closure type for a non-generic lambda-expression has a public inline function call operator (13.5.4 [over.call]) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively.

This is insufficiently precise because the trailing-return-type might itself contain a parameter-declaration-clause. (The same problem also occurs for generic lambdas later in the same paragraph.)




2011. Unclear effect of reference capture of reference

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Ville Voutilainen     Date: 2014-09-28

The Standard refers to capturing “entities,” and a reference is an entity. However, it is not clear what capturing a reference by reference would mean. In particular, 5.1.5 [expr.prim.lambda] paragraph 16 says,

It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference.

If a reference captured by reference is not represented by a member, it is hard to see how something like the following example could work:

  #include <functional>
  #include <iostream>

  std::function<void()> make_function(int& x) {
    return [&]{ std::cout << x << std::endl; };
  }

  int main() {
    int i = 3;
    auto f = make_function(i);
    i = 5;
    f();
  }

Should this be undefined behavior or should it print 5?

Proposed resolution (November, 2014):

  1. Change 5.1.5 [expr.prim.lambda] paragraph 18 as follows:

  2. Every id-expression within the compound-statement of a lambda-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] An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference. [Note: Such odr-uses are not invalidated by the end of the captured reference's lifetime. —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
        };
      }
      auto h(int &r) {
        return [&]() {
          ++r;      // Valid after h returns if the lifetime of the
                    // object to which r is bound has not ended
        };
      }
    

    end example]

  3. Change 5.1.5 [expr.prim.lambda] paragraph 23 as follows:

  4. [Note: If an a non-reference entity is implicitly or explicitly captured by reference, invoking the function call operator of the corresponding lambda-expression after the lifetime of the entity has ended is likely to result in undefined behavior. —end note]



2086. Reference odr-use vs implicit capture

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: Hubert Tong     Date: 2015-02-14

Whether a reference is odr-used or not has less to do with the context where it is named and more to do with its initializer. In particular, 5.1.5 [expr.prim.lambda] bullet 12.2 leads to cases where references that can never be odr-used are implicitly captured:

A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:

For example, ref should not be captured in the following:

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

  constexpr bool bar(int &, const A &a) { return &a == &globalA; }

  int main() {
    A &ref = globalA;
    [=](auto q) { static_assert(bar(q, ref), ""); }(0);
  }



2121. More flexible lambda syntax

Section: 5.1.5  [expr.prim.lambda]     Status: drafting     Submitter: EWG     Date: 2015-05-06

The grammar in 5.1.5 [expr.prim.lambda] paragraph 1 allows for omitting the the parameter list but only for a non-mutable lambda, i.e., it does not permit

    auto lambda = [] mutable { };

This should be addressed, and the possibility of other abbreviated forms should be considered, such as:

    [] -> float { return 42; }
    [] noexcept { foo(); }

(This is EWG issue 135.)

Proposed resolution (May, 2015):

  1. Change the grammar in 5.1.5 [expr.prim.lambda] paragraph 1 as follows:

  2. Change 5.1.5 [expr.prim.lambda] paragraph 4 as follows:

  3. If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). The lambda return type...
  4. Change 5.1.5 [expr.prim.lambda] paragraph 5 as follows:

  5. The closure type for a non-generic lambda-expression has a public inline function call operator (13.5.4 [over.call]) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively. For a generic lambda... This function call operator or operator template is declared const (9.2.2 [class.mfct.non-static]) if and only if the lambda-expression's parameter-declaration-clause is not followed by lambda-declarator does not contain the keyword mutable. It is neither...

Notes from the October, 2015 meeting:

Additional wording is needed in the proposed resolution in paragraph 5 to handle the potential absence of the parameter declaration clause.




1646. decltype-specifiers, abstract classes, and deduction failure

Section: 5.2.2  [expr.call]     Status: drafting     Submitter: Jason Merrill     Date: 2013-03-28

According to 5.2.2 [expr.call] paragraph 11,

If a function call is a prvalue of object type:

Thus, an example like

  template <class T> struct A: T { };
  template <class T> A<T> f(T) { return A<T>(); };
  decltype(f(42)) *p;

is well-formed. However, a function template specialization in which the return type is an abstract class should be a deduction failure, per 14.8.2 [temp.deduct] paragraph 8, last bullet:

The requirement that the return type in a function call in a decltype-specifier not be instantiated prevents the detection of this deduction failure in an example like:

  template <class T> struct A { virtual void f() = 0; };
  template <class T> A<T> f(T) { return A<T>(); };
  decltype(f(42)) *p;

It is not clear how this should be resolved.

(See also issue 1640.)




1880. When are parameter objects destroyed?

Section: 5.2.2  [expr.call]     Status: drafting     Submitter: Hubert Tong     Date: 2014-02-25

According to 5.2.2 [expr.call] paragraph 4,

The lifetime of a parameter ends when the function in which it is defined returns. The initialization and destruction of each parameter occurs within the context of the calling function.

This presumably means that the destruction of the parameter object occurs before the end of the full-expression, unlike temporaries. This is not what current implementations do, however. It is not clear that a change to treat parameter objects like temporaries, to match existing practice, would be an improvement, however, as it would result in ABI breakage for implementations that destroy parameters in the called function.

See also issue 1935 for a related question regarding the handling of arguments to a placement allocation function and placement deallocation function.

Notes from the June, 2014 meeting:

WG decided to make it unspecified whether parameter objects are destroyed immediately following the call or at the end of the full-expression to which the call belongs. This approach also resolves issue 1935.




1521. T{expr} with reference types

Section: 5.2.3  [expr.type.conv]     Status: drafting     Submitter: Steve Adamczyk     Date: 2012-07-10

According to 5.2.3 [expr.type.conv] paragraph 4,

Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.6.4 [dcl.init.list]) with the specified braced-init-list, and its value is that temporary object as a prvalue.

This wording does not handle the case where T is a reference type: it is not possible to create a temporary object of that type, and presumably the result would be an xvalue, not a prvalue.




1893. Function-style cast with braced-init-lists and empty pack expansions

Section: 5.2.3  [expr.type.conv]     Status: drafting     Submitter: Richard Smith     Date: 2014-03-13

According to 5.2.3 [expr.type.conv] paragraph 1,

A simple-type-specifier (7.1.7.2 [dcl.type.simple]) or typename-specifier (14.6 [temp.res]) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4 [expr.cast]). If the type specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.6 [dcl.init], 12.1 [class.ctor]), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.

This does not cover the cases when the expression-list contains a single braced-init-list (which is neither an expression nor more than a single value) or if it contains no expressions as the result of an empty pack expansion.

Proposed resolution (June, 2014): [SUPERSEDED]

This issue is resolved by the resolution of issue 1299.




1965. Explicit casts to reference types

Section: 5.2.7  [expr.dynamic.cast]     Status: drafting     Submitter: Richard Smith     Date: 2014-07-07

The specification of dynamic_cast in 5.2.7 [expr.dynamic.cast] paragraph 2 (and const_cast in 5.2.11 [expr.const.cast] is the same) says that the operand of a cast to an lvalue reference type must be an lvalue, so that

  struct A { virtual ~A(); }; A &&make_a();

  A &&a = dynamic_cast<A&&>(make_a());   // ok
  const A &b = dynamic_cast<const A&>(make_a()); // ill-formed

The behavior of static_cast is an odd hybrid:

  struct B : A { }; B &&make_b();
  A &&c = static_cast<A&&>(make_b()); // ok
  const A &d = static_cast<const A&>(make_b()); // ok
  const B &e = static_cast<const B&>(make_a()); // ill-formed

(Binding a const lvalue reference to an rvalue is permitted by 5.2.9 [expr.static.cast] paragraph 4 but not by paragraphs 2 and 3.)

There is implementation divergence on the treatment of these examples.

Also, const_cast permits binding an rvalue reference to a class prvalue but not to any other kind of prvalue, which seems like an unnecessary restriction.

Finally, 5.2.9 [expr.static.cast] paragraph 3 allows binding an rvalue reference to a class or array prvalue, but not to other kinds of prvalues; those are covered in paragraph 4. This would be less confusing if paragraph 3 only dealt with binding rvalue references to glvalues and left all discussion of prvalues to paragraph 4, which adequately handles the class and array cases as well.

Notes from the May, 2015 meeting:

CWG reaffirmed the status quo for dynamic_cast but felt that const_cast should be changed to permit binding an rvalue reference to types that have associated memory (class and array types).




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.11 [conv.ptr]), the typeid expression throws the bad_typeid exception (18.7.4 [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.11 [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.




1935. Reuse of placement arguments in deallocation

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: Hubert Tong     Date: 2014-06-04

The description in 5.3.4 [expr.new] paragraph 23 regarding calling a deallocation function following an exception during the initialization of an object resulting from a placement new-expression says,

If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax. If the implementation is allowed to make a copy of any argument as part of the call to the allocation function, it is allowed to make a copy (of the same original value) as part of the call to the deallocation function or to reuse the copy made as part of the call to the allocation function. If the copy is elided in one place, it need not be elided in the other.

This seems curious, as it allows reuse of a parameter object that presumably is destroyed immediately upon the return of the allocation function (but see issue 1880 for a question about the timing of such destructions).

Notes from the November, 2014 meeting:

The resolution for issue 1880 should mostly resolve this issue. The resolution should handle the case in which an object can only be constructed into the parameter object and neither copied nor moved.




2102. Constructor checking in new-expression

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: Richard Smith     Date: 2015-03-16

According to 5.3.4 [expr.new] paragraph 19,

If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (12.5 [class.free]), and the constructor (12.1 [class.ctor]).

The mention of “the constructor” here is strange. For the “object of class type” case, access and ambiguity control are done when we perform initialization in paragraph 17, and we might not be calling a constructor anyway (for aggregate initialization). This seems wrong.

For the “array of objects of class type” case, it makes slightly more sense (we need to check the trailing array elements can be default-initialized) but again (a) we aren't necessarily using a constructor, (b) we should say which constructor — and we may need overload resolution to find it, and (c) shouldn't this be part of initialization, so we can distinguish between the cases where we should copy-initialize from {} and the cases where we should default-initialize?




2112. new auto{x}

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: Richard Smith     Date: 2015-04-03

According to 5.3.4 [expr.new] paragraph 2,

If a placeholder type (7.1.7.4 [dcl.spec.auto]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain a new-initializer of the form

Now that auto v{x}; is permitted, this restriction on new-expressions should be revised to allow a single-element braced-init-list as well.




2177. Placement operator delete and parameter copies

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: Richard Smith     Date: 2015-09-30

Consider the following example:

  void *operator new(size_t n, std::string s) {
    std::string t = std::move(s);
    std::cout << "new " << t << std::endl;
    return operator new(n);
  }
  void operator delete(void*, std::string s) {
    std::cout << "delete " << s << std::endl;
  }
  struct X { X() { throw 0; } };
  int main() {
    try {
      new ("longer than the small string buffer") X();
    } catch (...) {}
  }

Current implementations print

  new longer than the small string buffer
  delete

because the same std::string object is used for both the new and delete calls. We should introduce additional copies to separate out the parameters in this case or make non-trivially-copyable parameter types ill-formed here.

Notes from the October, 2015 meeting:

CWG favored limiting the parameters of an overloaded deallocation function to trivially-copyable types.




2013. Pointer subtraction in large array

Section: 5.7  [expr.add]     Status: drafting     Submitter: Jason Merrill     Date: 2014-10-02

The common code sequence used by most implementations for pointer subtraction involves subtracting the pointer values to determine the number of bytes and then shifting to scale for the size of the array element. This produces incorrect results when the difference in bytes is larger than can be represented by a ptrdiff_t. For example, assuming a 32-bit ptrdiff_t:

  int *a, *b;
  a = malloc(0x21000000 * sizeof(int));
  b = a + 0x21000000;
  printf("%lx\n", (long)(b - a));

This will typically print e1000000 instead of 21000000.

Getting the right answer would require using a more expensive code sequence. It would be better to make this undefined behavior.




2182. Pointer arithmetic in array-like containers

Section: 5.7  [expr.add]     Status: drafting     Submitter: Jonathan Wakely     Date: 2015-10-20

The current direction for issue 1776 (see paper P0137) calls into question the validity of doing pointer arithmetic to address separately-allocated but contiguous objects in a container like std::vector. A related question is whether there should be some allowance made for allowing pointer arithmetic using a pointer to a base class if the derived class is a standard-layout class with no non-static data members. It is possible that std::launder could play a part in the resolution of this issue.

Notes from the February, 2016 meeting:

This issue is expected to be resolved by the resolution of issue 1776. The major problem is when the elements of the vector contain constant or reference members; 3.8 [basic.life] paragraph 7 implies that pointer arithmetic leading to such an object produces undefined behavior, and CWG expects this to continue. Some changes to the interface of std::vector may be required, perhaps using std::launder as part of iterator processing.




1857. Additional questions about bits

Section: 5.8  [expr.shift]     Status: drafting     Submitter: Tony Van Eerd     Date: 2014-02-12

The resolution of issue 1796 addressed only the relationship of “bits” with the null character value. The values and arrangements of bits within an object are also mentioned in other contexts; these should also be considered for revision. For example, 5.8 [expr.shift] paragraph 2 says,

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled.

This appears to place constraints on the bit representation, which (as noted in issue 1796) is not accessible to the program. A similar statement appears in paragraph 3 for >>.

The specification of the bitwise operations in 5.11 [expr.bit.and], 5.12 [expr.xor], and 5.13 [expr.or] uses the undefined term “bitwise” in describing the operations, without specifying whether it is the value or object representation that is in view.

Part of the resolution of this might be to define “bit” (which is otherwise currently undefined in C++) as a value of a given power of 2.

Notes from the June, 2014 meeting:

CWG decided to reformulate the description of the operations themselves to avoid references to bits, splitting off the larger questions of defining “bit” and the like to issue 1943 for further consideration.




2023. Composite reference result type of conditional operator

Section: 5.16  [expr.cond]     Status: drafting     Submitter: Daniel Krügler     Date: 2014-10-16

The conditional operator converts pointer operands to their composite pointer type (5.16 [expr.cond] bullets 6.3 and 6.4). Similar treatment should be afforded to operands of reference type.

See also issue 2018.




1542. Compound assignment of braced-init-list

Section: 5.18  [expr.ass]     Status: drafting     Submitter: Mike Miller     Date: 2012-08-21

The specification of 5.18 [expr.ass] paragraph 9 is presumably intended to allow use of a braced-init-list as the operand of a compound assignment operator as well as a simple assignment operator, although the normative wording does not explicitly say so. (The example in that paragraph does include

  complex<double> z;
  z += { 1, 2 };      // meaning z.operator+=({1,2})

for instance, which could be read to imply compound assignment operators for scalar types as well.)

However, the details of how this is to be implemented are not clear. Paragraph 7 says,

The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

Applying this pattern literally to a braced-init-list yields invalid code: x += {1} would become x = x + {1}, which is non-syntactic.

Another problem is how to apply the prohibition against narrowing conversions to a compound assignment. For example,

  char c;
  c += {1};

would presumably always be a narrowing error, because after integral promotions, the type of c+1 is int. The similar issue 1078 was classified as "NAD" because the workaround was simply to add a cast to suppress the error; however, there is no place to put a similar cast in a compound assignment.

Notes from the October, 2012 meeting:

The incorrect description of the meaning of a compound assignment with a braced-init-list should be fixed by CWG. The question of whether it makes sense to apply narrowing rules to such assignments is better addressed by EWG.




1255. Definition problems with constexpr functions

Section: 5.20  [expr.const]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-08

The current wording of the Standard is not sufficiently clear regarding the interaction of class scope (which treats the bodies of member functions as effectively appearing after the class definition is complete) and the use of constexpr member functions within the class definition in contexts requiring constant expressions. For example, an array bound cannot use a constexpr member function that relies on the completeness of the class or on members that have not yet been declared, but the current wording does not appear to state that.

Additional note (October, 2013):

This question also affects function return type deduction (the auto specifier) in member functions. For example, the following should presumably be prohibited, but the current wording is not clear:

  struct S {
    static auto f() {
      return 42;
    }
    auto g() -> decltype(f()) {
      return f();
    }
  };



1452. Value-initialized objects may be constants

Section: 5.20  [expr.const]     Status: drafting     Submitter: Richard Smith     Date: 2011-12-24

Some classes that would produce a constant when initialized by value-initialization are not considered literal types. For example:

   struct A { int a; }; // non-constexpr default constructor
   struct B : A {};     // non-literal type



1626. constexpr member functions in brace-or-equal-initializers

Section: 5.20  [expr.const]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2013-02-19

The Standard should make clear that a constexpr member function cannot be used in a constant expression until its class is complete. For example:

  template<typename T> struct C {
    template<typename T2> static constexpr bool _S_chk() {
      return false;
    }
    static const bool __value = _S_chk<int>();
  }; 

  C<double> c;

Current implementations accept this, although they reject the corresponding non-template case:

  struct C {
    static constexpr bool _S_chk() { return false; }
    static const bool __value = _S_chk();
  };

  C c; 

Presumably the template case should be handled consistently with the non-template case.




2126. Lifetime-extended temporaries in constant expressions

Section: 5.20  [expr.const]     Status: drafting     Submitter: Richard Smith     Date: 2015-05-20

Consider an example like the following:

  typedef const int CI[3];
  constexpr CI &ci = CI{11, 22, 33};
  static_assert(ci[1] == 22, "");

This is ill-formed because the lifetime of the array temporary did not start within the current evaluation. Perhaps we should treat all lifetime-extended temporaries of const-qualified literal type that are initialized by constant expressions as if they are constexpr objects?




2166. Unclear meaning of “undefined constexpr function”

Section: 5.20  [expr.const]     Status: drafting     Submitter: Howard Hinnant     Date: 2015-08-05

According to 5.20 [expr.const] bullet 2.3, an expression is a constant expression unless (among other reasons) it would evaluate

This does not address the question of the point at which a constexpr function must be defined. The intent, in order to allow mutually-recursive constexpr functions, was that the function must be defined prior to the outermost evaluation that eventually results in the invocation, but this is not clearly stated.




2186. Unclear point that “preceding initialization” must precede

Section: 5.20  [expr.const]     Status: drafting     Submitter: Hubert Tong     Date: 2015-10-24

Similar to the concern of issue 2166, the requirement of 5.20 [expr.const] bullet 2.7.1 for

does not specify the point at which the determination of “preceding initialization” is made: is it at the point at which the reference to the variable appears lexically, or is it the point at which the outermost constant evaluation occurs? There is implementation divergence on this point.




1523. Point of declaration in range-based for

Section: 6.5.4  [stmt.ranged]     Status: drafting     Submitter: John Spicer     Date: 2012-07-16

According to the general rule for declarations in 3.3.2 [basic.scope.pdecl] paragraph 1,

The point of declaration for a name is immediately after its complete declarator (Clause 8 [dcl.decl]) and before its initializer (if any), except as noted below.

However, the rewritten expansion of the range-based for statement in 6.5.4 [stmt.ranged] paragraph 1 contradicts this general rule, so that the index variable is not visible in the range-init:

  for (int i : {i}) ;   // error: i not in scope

(See also issue 1498 for another question regarding the rewritten form of the range-based for.)

Notes from the October, 2012 meeting:

EWG is discussing issue 900 and the outcome of that discussion should be taken into consideration in addressing this issue.

Notes from the April, 2013 meeting:

The approach favored by CWG for resolving this issue is to change the point of declaration of the variable in the for-range-declaration to be after the ).




1680. Including <initializer_list> for range-based for

Section: 6.5.4  [stmt.ranged]     Status: drafting     Submitter: Richard Smith     Date: 2013-05-13

A simple example like

  int main() {
    int k = 0;
    for (auto x : { 1, 2, 3 })
      k += x;
    return k;
  }

requires that the <initializer_list> header be included, because the expansion of the range-based for involves a declaration of the form

  auto &&__range = { 1, 2, 3 };

and a braced-init-list causes auto to be deduced as a specialization of std::initializer_list. This seems unnecessary and could be eliminated by specifying that __range has an array type for cases like this.

(It should be noted that EWG is considering a proposal to change auto deduction for cases involving braced-init-lists, so resolution of this issue should be coordinated with that effort.)

Notes from the September, 2013 meeting:

CWG felt that this issue should be resolved by using the array variant of the range-based for implementation.




2115. Order of implicit destruction vs release of automatic storage

Section: 6.6  [stmt.jump]     Status: drafting     Submitter: Richard Smith     Date: 2015-04-16

The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?

Notes from the February, 2016 meeting:

CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.




1223. Syntactic disambiguation and trailing-return-types

Section: 6.8  [stmt.ambig]     Status: drafting     Submitter: Michael Wong     Date: 2010-11-08

Because the restriction that a trailing-return-type can appear only in a declaration with “the single type-specifier auto” (8.3.5 [dcl.fct] paragraph 2) is a semantic, not a syntactic, restriction, it does not influence disambiguation, which is “purely syntactic” (6.8 [stmt.ambig] paragraph 3). Consequently, some previously unambiguous expressions are now ambiguous. For example:

struct A {
  A(int *);
  A *operator()(void);
  int B;
};
 
int *p;
typedef struct BB { int C[2]; } *B, C;
 
void foo() {
// The following line becomes invalid under C++0x:
  A (p)()->B;  // ill-formed function declaration
 
// In the following,
// - B()->C is either type-id or class member access expression
// - B()->C[1] is either type-id or subscripting expression
// N3126 subclause 8.2 [dcl.ambig.res] does not mention an ambiguity
// with these forms of expression
  A a(B ()->C);  // function declaration or object declaration
  sizeof(B ()->C[1]);  // sizeof(type-id) or sizeof on an expression
}

Notes from the March, 2011 meeting:

CWG agreed that the presence of auto should be considered in disambiguation, even though it is formally handled semantically rather than syntactically.




1616. Disambiguation parsing and template parameters

Section: 6.8  [stmt.ambig]     Status: drafting     Submitter: Johannes Schaub     Date: 2013-02-01

According to 6.8 [stmt.ambig] paragraph 3,

The disambiguation is purely syntactic; that is, the meaning of the names occurring in such a statement, beyond whether they are type-names or not, is not generally used in or changed by the disambiguation. Class templates are instantiated as necessary to determine if a qualified name is a type-name. Disambiguation precedes parsing, and a statement disambiguated as a declaration may be an ill-formed declaration. If, during parsing, a name in a template parameter is bound differently than it would be bound during a trial parse, the program is ill-formed. No diagnostic is required. [Note: This can occur only when the name is declared earlier in the declaration. —end note]

The statement about template parameters is confusing (and not helped by the fact that the example that follows illustrates the general rule for declarations and does not involve any template parameters). It is attempting to say that a program is ill-formed if a template argument of a class template specialization has a different value in the two parses. With decltype this can now apply to other kinds of templates as well, so the wording should be clarified and made more general.




2117. Explicit specializations and constexpr function templates

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Faisal Vali     Date: 2015-04-26

According to 7.1.5 [dcl.constexpr] paragraph 6,

If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed; no diagnostic required.

This should say “instantiated template specialization” instead of just “specialization” to clarify that an explicit specialization is not in view here.




1348. Use of auto in a trailing-return-type

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: Richard Smith     Date: 2011-08-16

It is not clear whether the auto specifier can appear in a trailing-return-type.




1670. auto as conversion-type-id

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: Richard Smith     Date: 2013-04-26

N3690 comment FI 4

The current wording allows something like

  struct S {
    operator auto() { return 0; }
  } s;

If it is intended to be permitted, the details of its handling are not clear. Also, a similar syntax has been discussed as a possible future extension for dealing with proxy types in deduction which, if adopted, could cause confusion.

Additional note, November, 2013:

Doubt was expressed during the 2013-11-25 drafting review teleconference as to the usefulness of this provision. It is therefore being left open for further consideration after C++14 is finalized.

Notes from the February, 2014 meeting:

CWG continued to express doubt as to the usefulness of this construct but felt that if it is permitted, the rules need clarification.




1868. Meaning of “placeholder type”

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: Dawn Perchik     Date: 2014-02-13

7.1.7 [dcl.type] paragraph 2 describes the auto specifier as “a placeholder for a type to be deduced.” Elsewhere, the Standard refers to the type represented by the auto specifier as a “placeholder type.” This usage has been deemed confusing by some, requiring either a definition of one or both terms or rewording to avoid them.




2053. auto in non-generic lambdas

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: Faisal Vali     Date: 2014-12-07

According to 7.1.7.4 [dcl.spec.auto] paragraph 3,

If the auto type-specifier appears as one of the decl-specifiers in the decl-specifier-seq of a parameter-declaration of a lambda-expression, the lambda is a generic lambda (5.1.5 [expr.prim.lambda]).

and 5.1.5 [expr.prim.lambda] paragraph 5 says,

For a generic lambda, the closure type has a public inline function call operator member template (14.5.2 [temp.mem]) whose template-parameter-list consists of one invented type template-parameter for each occurrence of auto in the lambda's parameter-declaration-clause, in order of appearance.

However, an auto that signals a trailing-return-type should be excluded from these descriptions.




2059. Linkage and deduced return types

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: Richard Smith     Date: 2014-12-15

Use of function return type deduction makes it possible to define functions whose return type is a type without linkage. Although 3.5 [basic.link] paragraph 8 permits such a usage if the function is defined in the same translation unit as it is used, it may be helpful to consider changing the overall rules regarding the use of types with internal or no linkage. As an example, the following example permits access to a local static variable that has not been initialized:

  auto f() {
    static int n = 123;
    struct X { int &f() { return n; } };
    return X();
  }
  int &r = decltype(f())().f();

Notes from the February, 2016 meeting:

CWS agreed that the current rule in 3.5 [basic.link] paragraph 8 is unneeded; the ODR already prohibits use of an entity that is not defined in the current translation unit and cannot be defined in a different translation unit.




2081. Deduced return type in redeclaration or specialization of function template

Section: 7.1.7.4  [dcl.spec.auto]     Status: drafting     Submitter: John Spicer     Date: 2015-02-05

7.1.7.4 [dcl.spec.auto] paragraph 13 says,

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.

The inverse should also be true (a specialization cannot use a placeholder type if the template used a non-placeholder), but this is not said explicitly.




1485. Out-of-class definition of member unscoped opaque enumeration

Section: 7.2  [dcl.enum]     Status: drafting     Submitter: Richard Smith     Date: 2012-03-26

The scope in which the names of enumerators are entered for a member unscoped opaque enumeration is not clear. According to 7.2 [dcl.enum] paragraph 10,

Each enum-name and each unscoped enumerator is declared in the scope that immediately contains the enum-specifier.

In the case of a member opaque enumeration defined outside its containing class, however, it is not clear whether the enumerator names are declared in the class scope or in the lexical scope containing the definition. Declaring them in the class scope would be a violation of 9.2 [class.mem] paragraph 1:

The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere.

Declaring the names in the lexical scope containing the definition would be contrary to the example in 14.5.1.4 [temp.mem.enum] paragraph 1:

  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;

There also appear to be problems with the rules for dependent types and members of the current instantiation.




1636. Bits required for negative enumerator values

Section: 7.2  [dcl.enum]     Status: drafting     Submitter: Hyman Rosen     Date: 2013-03-07

According to 7.2 [dcl.enum] paragraph 7,

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|-K,|emax|) and equal to 2M-1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise.

The result of these calculations is that the number of bits required for

  enum { N = -1, Z = 0 }

is 1, but the number required for

  enum { N = -1 }

is 2. This is surprising. This could be fixed by changing |emax| to emax.




1917. decltype-qualified enumeration names

Section: 7.2  [dcl.enum]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2014-04-25

8.3 [dcl.meaning] paragraph 1 and 9 [class] paragraph 11 prohibit decltype-qualified declarators and class names, respectively. There is no such prohibition in 7.2 [dcl.enum] for enumeration names. Presumably that is an oversight that should be rectified.




2131. Ambiguity with opaque-enum-declaration

Section: 7.2  [dcl.enum]     Status: drafting     Submitter: Richard Smith     Date: 2015-05-28

The declaration

  enum E;

is ambiguous: it could be either a simple-declaration comprising the elaborated-type-specifier enum E and no init-declarator-list, or it could be an opaque-enum-declaration with an omitted enum-base (both of which are ill-formed, for different reasons).




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.)




386. Friend declaration of name brought in by using-declaration

Section: 7.3.3  [namespace.udecl]     Status: drafting     Submitter: Herb Sutter     Date: 8 Oct 2002

The following came up recently on comp.lang.c++.moderated (edited for brevity):

  namespace N1 {
    template<typename T> void f( T* x ) {
      // ... other stuff ...
      delete x;
    }
  }

  namespace N2 {
    using N1::f;

    template<> void f<int>( int* ); // A: ill-formed

    class Test {
      ~Test() { }
      friend void f<>( Test* x );   // B: ill-formed?
    };
  }

I strongly suspect, but don't have standardese to prove, that the friend declaration in line B is ill-formed. Can someone show me the text that allows or disallows line B?

Here's my reasoning: Writing "using" to pull the name into namespace N2 merely allows code in N2 to use the name in a call without qualification (per 7.3.3 [namespace.udecl]). But just as declaring a specialization must be done in the namespace where the template really lives (hence line A is ill-formed), I suspect that declaring a specialization as a friend must likewise be done using the original namespace name, not obliquely through a "using". I see nothing in 7.3.3 [namespace.udecl] that would permit this use. Is there?

Andrey Tarasevich: 14.5.4 [temp.friend] paragraph 2 seems to get pretty close: "A friend declaration that is not a template declaration and in which the name of the friend is an unqualified 'template-id' shall refer to a specialization of a function template declared in the nearest enclosing namespace scope".

Herb Sutter: OK, thanks. Then the question in this is the word "declared" -- in particular, we already know we cannot declare a specialization of a template in any other namespace but the original one.

John Spicer: This seems like a simple question, but it isn't.

First of all, I don't think the standard comments on this usage one way or the other.

A similar example using a namespace qualified name is ill-formed based on 8.3 [dcl.meaning] paragraph 1:

  namespace N1 {
        void f();
  }

  namespace N2 {
        using N1::f;
        class A {
                friend void N2::f();
        };
  }

Core issue 138 deals with this example:

  void foo();
  namespace A{
    using ::foo;
    class X{
      friend void foo();
    };
  }

The proposed resolution (not yet approved) for issue 138 is that the friend declares a new foo that conflicts with the using-declaration and results in an error.

Your example is different than this though because the presence of the explicit argument list means that this is not declaring a new f but is instead using a previously declared f.

One reservation I have about allowing the example is the desire to have consistent rules for all of the "declaration like" uses of template functions. Issue 275 (in DR status) addresses the issue of unqualified names in explicit instantiation and explicit specialization declarations. It requires that such declarations refer to templates from the namespace containing the explicit instantiation or explicit specialization. I believe this rule is necessary for those directives but is not really required for friend declarations -- but there is the consistency issue.

Notes from April 2003 meeting:

This is related to issue 138. John Spicer is supposed to update his paper on this topic. This is a new case not covered in that paper. We agreed that the B line should be allowed.




1907. using-declarations and default arguments

Section: 7.3.3  [namespace.udecl]     Status: drafting     Submitter: Richard Smith     Date: 2014-03-30

The status of an example like the following is not clear:

  void f(int, int);
  template<typename T> void g(T t) { f(t); }
  void f(int, int = 0);
  void h() { g(0); }

According to 14.6.4 [temp.dep.res] paragraph 1,

In resolving dependent names, names from the following sources are considered:

If this is to be interpreted as meaning that only the declarations that are visible at the point of definition can be used in overload resolution for dependent calls, the call g(0) is ill-formed. If, however, it is the names, not the declarations, that are captured, then presumably the second declaration of f should be considered, making the call well-formed. There is implementation divergence for this example.

The resolution of issue 1551 recently clarified the requirements in similar cases involving using-declarations:

  namespace N { void f(int, int); }
  using N::f;
  template<typename T> void g(T t) { f(t); }
  namespace N { void f(int, int = 0); }
  void h() { g(0); }

The note added to 7.3.3 [namespace.udecl] paragraph 11 makes clear that the call g(0) is well-formed in this example.

This outcome results in an unfortunate discrepancy between how default arguments and overloaded functions are treated, even though default arguments could conceptually be viewed as simply adding extra overloads for the additional arguments.

Notes from the June, 2014 meeting:

CWG was unable to come to consensus regarding the desired outcome, with an approximately equal split between desiring the first example to be well-formed or ill-formed. It was noted that the resolution of issue 1850 makes the corresponding case for non-dependent references ill-formed, with no diagnostic required. Similar questions also apply to completing an array type, which also involves a modification to an existing entity declaration in a given scope.

Notes from the February, 2016 meeting:

CWG determined that the case should be ill-formed, no diagnostic required, to allow implementations to continue to use either strategy.




1713. Linkage of variable template specializations

Section: 7.5  [dcl.link]     Status: drafting     Submitter: Richard Smith     Date: 2013-07-08

Given a namespace-scope declaration like

  template<typename T> T var = T();

should T<const int> have internal linkage by virtue of its const-qualified type? Or should it inherit the linkage of the template?

Notes from the February, 2014 meeting:

CWG noted that linkage is by name, and a specialization of a variable template does not have a name separate from that of the variable template, thus the specialization will have the linkage of the template.




1817. Linkage specifications and nested scopes

Section: 7.5  [dcl.link]     Status: drafting     Submitter: Richard Smith     Date: 2013-12-04

According to 7 [dcl.dcl] paragraph 2,

Unless otherwise stated, utterances in Clause 7 [dcl.dcl] about components in, of, or contained by a declaration or subcomponent thereof refer only to those components of the declaration that are not nested within scopes nested within the declaration.

This contradicts the intent of 7.5 [dcl.link] paragraph 4, which says,

In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification.

Also, one of the comments in the example in paragraph 4 is inconsistent with the intent:

  extern "C" {
    static void f4(); // the name of the function f4 has
                      // internal linkage (not C language
                      // linkage) and the function's type
                      // has C language linkage.
  }

  extern "C" void f5() {
    extern void f4(); // OK: Name linkage (internal)
                      // and function type linkage (C
                      // language linkage) gotten from
                      // previous declaration.
  }

The language linkage for the block-scope declaration of f4 is presumably determined by the fact that it appears in a C-linkage function, not by the previous declaration.

Proposed resolution (February, 2014):

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

Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope (3.3 [basic.scope]). In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification, including those appearing in scopes nested inside the linkage specification and not inside a nested linkage-specification. [Example:

...

  extern "C" {
    static void f4(); // the name of the function f4 has
                      // internal linkage (not C language
                      // linkage) and the function's type
                      // has C language linkage.
  }

  extern "C" void f5() {
    extern void f4(); // OK: Name linkage (internal)
                      // and function type linkage (C
                      // language linkage) gotten from
                      // previous declaration.; function type
                      // linkage (C language
                      // linkage) gotten
                      // from linkage specification
  }

Additional note, November, 2014:

The issue has been returned to "drafting" status to clarify the circumstances under which a preceding declaration supplies the language linkage for a declaration (for example, not when the declaration uses a typedef, which carries the language linkage, but only when the declaration uses a function declarator).




1706. alignas pack expansion syntax

Section: 7.6.1  [dcl.attr.grammar]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2013-06-26

The grammar for alignment-specifier in 7.6.1 [dcl.attr.grammar] paragraph 1 is:

where the ellipsis indicates pack expansion. Naively, one would expect that the expansion would result in forms like

    alignas()
    alignas(1, 2)
    alignas(int, double) 

but none of those forms is given any meaning by the current wording. Instead, 14.5.3 [temp.variadic] paragraph 4 says,

In an alignment-specifier (7.6.2 [dcl.align]); the pattern is the alignment-specifier without the ellipsis.

Presumably this means that something like alignas(T...) would expand to something like

    alignas(int) alignas(double)

This is counterintuitive and should be reexamined.

See also messages 24016 through 24021.

Notes from the February, 2014 meeting:

CWG decided to change the pack expansion of alignas so that the type-id or assignment-expression is repeated inside the parentheses and to change the definition of alignas to accept multiple arguments with the same meaning as multiple alignas specifiers.




1342. Order of initialization with multiple declarators

Section: 8  [dcl.decl]     Status: drafting     Submitter: Alberto Ganesh Barbati     Date: 2011-08-11

It is not clear what, if anything, in the existing specification requires that the initialization of multiple init-declarators within a single declaration be performed in declaration order.




1488. abstract-pack-declarators in type-ids

Section: 8.1  [dcl.name]     Status: drafting     Submitter: Richard Smith     Date: 2012-03-28

The grammar for type-id in 9.1 [class.name] paragraph 1 has two problems. First, the fact that we allow an abstract-pack-declarator makes some uses of type-id (template arguments, alignment specifiers, exception-specifications) ambiguous: T... could be parsed either as a type-id, including the ellipsis, or as the type-id T with a following ellipsis. There does not appear to be any rule to disambiguate these parses.

The other problem is that we do not allow parentheses in an abstract-pack-declarator, which makes

  template<typename...Ts> void f(Ts (&...)[4]);

ill-formed because (&...)() is not an abstract-pack-declarator. There is implementation variance on this point.




1900. Do friend declarations count as “previous declarations”?

Section: 8.3  [dcl.meaning]     Status: drafting     Submitter: Hubert Tong     Date: 2014-03-25

Issue 1477 assumes that a name declared only in a friend declaration can be defined outside its namespace using a qualified-id, but the normative passages in 8.3 [dcl.meaning] paragraph 1 and 7.3.1.2 [namespace.memdef] paragraph 2 do not settle the question definitively, and there is implementation variance. A clearer statement of intent is needed.




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.6.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.2.4 [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.6.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.2.4 [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.

Note, January, 2012:

The resolution should also probably deal with the fact that the “one-past-the-end” address of an array does not designate a valid object (even if such a pointer might “point to” an object of the correct type, per 3.9.2 [basic.compound]) and thus is not suuitable for the lvalue-to-rvalue conversion.




1640. Array of abstract instance of class template

Section: 8.3.4  [dcl.array]     Status: drafting     Submitter: Richard Smith     Date: 2013-03-14

According to 8.3.4 [dcl.array] paragraph 1, an array declarator whose element type is an abstract class is ill-formed. However, if the element type is a class template specialization, it may not be known that the class is abstract; because forming an array of an incomplete type is permitted (3.9 [basic.types] paragraphs 5-6), the class template is not required to be instantiated in order to use it as an element type. The expected handling if the class template is later instantiated is unclear; should the compiler issue an error about the earlier array array type at the point at which the class template is instantiated?

This also affects overload resolution:

  template<typename> struct Abstract {
    virtual void f() = 0;
    typedef int type;
  };
  template<typename T> char &abstract_test(T[1]);      // #1
  template<typename T> char (&abstract_test(...))[2];  // #2
  // Abstract<int>::type n;
  static_assert(sizeof(abstract_test<Abstract<int>>(nullptr)) == 2, "");

Overload resolution will select #1 and fail the assertion; if the commented line is uncommented, there is implementation variance, but presumably #2 should be selected and satisfy the assertion.

These effects of completing the type are troublesome. Would it be better to allow array types of abstract element type and simply prohibit creation of objects of such arrays?

(See also issue 1646.)




1001. Parameter type adjustment in dependent parameter types

Section: 8.3.5  [dcl.fct]     Status: drafting     Submitter: Jason Merrill     Date: 2009-11-08

According to 8.3.5 [dcl.fct] paragraph 5, top-level cv-qualifiers on parameter types are deleted when determining the function type. It is not clear how or whether this adjustment should be applied to parameters of function templates when the parameter has a dependent type, however. For example:

    template<class T> struct A {
       typedef T arr[3];
    };

    template<class T> void f(const typename A<T>::arr) { } // #1

    template void f<int>(const A<int>::arr);

    template <class T> struct B {
       void g(T);
    };

    template <class T> void B<T>::g(const T) { } // #2

If the const in #1 is dropped, f<int> has a parameter type of A* rather than the const A* specified in the explicit instantiation. If the const in #2 is not dropped, we fail to match the definition of B::g to its declaration.

Rationale (November, 2010):

The CWG agreed that this behavior is intrinsic to the different ways cv-qualification applies to array types and non-array types.

Notes, January, 2012:

Additional discussion of this issue arose regarding the following example:

    template<class T> struct A {
      typedef double Point[2];
      virtual double calculate(const Point point) const = 0;
    };

    template<class T> struct B : public A<T> {
      virtual double calculate(const typename A<T>::Point point) const {
        return point[0];
      }
    };

    int main() {
      B<int> b;
      return 0;
    }

The question is whether the member function in B<int> has the same type as that in A<int>: is the parameter-type-list instantiated directly (i.e., using the adjusted types) or regenerated from the individual parameter types?

(See also issue 1322.)




1668. Parameter type determination still not clear enough

Section: 8.3.5  [dcl.fct]     Status: drafting     Submitter: Daniel Krügler     Date: 2013-04-25

According to 8.3.5 [dcl.fct] paragraph 5,

The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. —end note]

This is not sufficiently clear to specify the intended handling of an example like

  void f(int a[10], decltype(a) *p );

Should the type of p be int(*)[10] or int**? The latter is the intended result, but the phrase “after determining the type of each parameter” makes it sound as if the adjustments are performed after all the parameter types have been determined from the decl-specifier-seq and declarator instead of for each parameter individually.

See also issue 1444.




325. When are default arguments parsed?

Section: 8.3.6  [dcl.fct.default]     Status: drafting     Submitter: Nathan Sidwell     Date: 27 Nov 2001

The standard is not precise enough about when the default arguments of member functions are parsed. This leads to confusion over whether certain constructs are legal or not, and the validity of certain compiler implementation algorithms.

8.3.6 [dcl.fct.default] paragraph 5 says "names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears"

However, further on at paragraph 9 in the same section there is an example, where the salient parts are

  int b;
  class X {
    int mem2 (int i = b); // OK use X::b
    static int b;
  };
which appears to contradict the former constraint. At the point the default argument expression appears in the definition of X, X::b has not been declared, so one would expect ::b to be bound. This of course appears to violate 3.3.7 [basic.scope.class] paragraph 1(2) "A name N used in a class S shall refer to the same declaration in its context and when reevaluated in the complete scope of S. No diagnostic is required."

Furthermore 3.3.7 [basic.scope.class] paragraph 1(1) gives the scope of names declared in class to "consist not only of the declarative region following the name's declarator, but also of .. default arguments ...". Thus implying that X::b is in scope in the default argument of X::mem2 previously.

That previous paragraph hints at an implementation technique of saving the token stream of a default argument expression and parsing it at the end of the class definition (much like the bodies of functions defined in the class). This is a technique employed by GCC and, from its behaviour, in the EDG front end. The standard leaves two things unspecified. Firstly, is a default argument expression permitted to call a static member function declared later in the class in such a way as to require evaluation of that function's default arguments? I.e. is the following well formed?

  class A {
    static int Foo (int i = Baz ());
    static int Baz (int i = Bar ());
    static int Bar (int i = 5);
 };
If that is well formed, at what point does the non-sensicalness of
  class B {
    static int Foo (int i = Baz ());
    static int Baz (int i = Foo());
  };
become detected? Is it when B is complete? Is it when B::Foo or B::Baz is called in such a way to require default argument expansion? Or is no diagnostic required?

The other problem is with collecting the tokens that form the default argument expression. Default arguments which contain template-ids with more than one parameter present a difficulty in determining when the default argument finishes. Consider,

  template <int A, typename B> struct T { static int i;};
  class C {
    int Foo (int i = T<1, int>::i);
  };
The default argument contains a non-parenthesized comma. Is it required that this comma is seen as part of the default argument expression and not the beginning of another of argument declaration? To accept this as part of the default argument would require name lookup of T (to determine that the '<' was part of a template argument list and not a less-than operator) before C is complete. Furthermore, the more pathological
  class D {
    int Foo (int i = T<1, int>::i);
    template <int A, typename B> struct T {static int i;};
  };
would be very hard to accept. Even though T is declared after Foo, T is in scope within Foo's default argument expression.

Suggested resolution:

Append the following text to 8.3.6 [dcl.fct.default] paragraph 8.

The default argument expression of a member function declared in the class definition consists of the sequence of tokens up until the next non-parenthesized, non-bracketed comma or close parenthesis. Furthermore such default argument expressions shall not require evaluation of a default argument of a function declared later in the class.

This would make the above A, B, C and D ill formed and is in line with the existing compiler practice that I am aware of.

Notes from the October, 2005 meeting:

The CWG agreed that the first example (A) is currently well-formed and that it is not unreasonable to expect implementations to handle it by processing default arguments recursively.

Additional notes, May, 2009:

Presumably the following is ill-formed:

    int f(int = f());

However, it is not clear what in the Standard makes it so. Perhaps there needs to be a statement to the effect that a default argument only becomes usable after the complete declarator of which it is a part.

Notes from the August, 2011 meeting:

In addition to default arguments, commas in template argument lists also cause problems in initializers for nonstatic data members:

    struct S {
      int n = T<a,b>(c);  // ill-formed declarator for member b
                          // or template argument?
    };

(This is from #16 of the IssuesFoundImplementingC0x.pdf document on the Bloomington wiki.

Additional notes (August, 2011):

See also issues 1352 and 361.

Notes from the February, 2012 meeting:

It was decided to handle the question of parsing an initializer like T<a,b>(c) (a template-id or two declarators) in this issue and the remaining questions in issue 361. For this issue, a template-id will only be recognized if there is a preceding declaration of a template.




1580. Default arguments in explicit instantiations

Section: 8.3.6  [dcl.fct.default]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2012-10-29

It is not clear, either from 8.3.6 [dcl.fct.default] or 14.7.2 [temp.explicit], whether it is permitted to add a default argument in an explicit instantiation of a function template:

  template<typename T> void f(T, int) { }
  template void f<int>(int, int=0);  // Permitted?

Notes from the April, 2013 meeting:

The intent is to prohibit default arguments in explicit instantiations.




1962. Type of __func__

Section: 8.4.1  [dcl.fct.def.general]     Status: drafting     Submitter: Steve Clamage     Date: 2014-07-04

Two questions have arisen regarding the treatment of the type of the __func__ built-in variable. First, some implementations accept

  void f() {
    typedef decltype(__func__) T;
    T x = __func__;
  }

even though T is specified to be an array type.

In a related question, it was noted that __func__ is implicitly required to be unique in each function, and that not only the value but the type of __func__ are implementation-defined; e.g., in something like

  inline auto f() { return &__func__; }

the function type is implementation-specific. These concerns could be addressed by making the value a prvalue of type const char* instead of an array lvalue.

Notes from the May, 2015 meeting:

CWG agreed with the suggested direction.




2144. Function/variable declaration ambiguity

Section: 8.4.1  [dcl.fct.def.general]     Status: drafting     Submitter: Richard Smith     Date: 2015-06-19

The following fragment,

  int f() {};

is syntactically ambiguous. It could be either a function-definition followed by an empty-declaration, or it could be a simple-declaration whose init-declarator has the brace-or-equal-initializer {}. The same is true of a variable declaration

  int a {};

since function-definition simply uses the term declarator in its production.




1733. Return type and value for operator= with ref-qualifier

Section: 8.4.2  [dcl.fct.def.default]     Status: drafting     Submitter: James Widman     Date: 2013-08-09

8.4.2 [dcl.fct.def.default] paragraph 1 specifies that an explicitly-defaulted function shall

have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function's class) as if it had been implicitly declared...

This allows an example like

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

but forbids

  struct B {
    B&& operator=(B const&) && = default; 
  };

which seems backward.

In addition, 12.8 [class.copy] paragraph 22 only specifies the return value for implicitly-declared copy/move assignment operators, not for explicitly-defaulted ones.




1854. Disallowing use of implicitly-deleted functions

Section: 8.4.2  [dcl.fct.def.default]     Status: drafting     Submitter: Richard Smith     Date: 2014-02-11

The resolution of issue 1778 means that whether an explicitly-defaulted function is deleted or not cannot be known until the end of the class definition. As a result, new rules are required to disallow references (in, e.g., decltype) to explicitly-defaulted functions that might later become deleted.

Notes from the June, 2014 meeting:

The approach favored by CWG was to make any reference to an explicitly-defaulted function ill-formed if it occurs prior to the end of the class definition.




253. Why must empty or fully-initialized const objects be initialized?

Section: 8.6  [dcl.init]     Status: drafting     Submitter: Mike Miller     Date: 11 Jul 2000

Paragraph 9 of 8.6 [dcl.init] says:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.

What if a const POD object has no non-static data members? This wording requires an empty initializer for such cases:

    struct Z {
        // no data members
        operator int() const { return 0; }
    };

    void f() {
        const Z z1;         // ill-formed: no initializer
        const Z z2 = { };   // well-formed
    }

Similar comments apply to a non-POD const object, all of whose non-static data members and base class subobjects have default constructors. Why should the class of such an object be required to have a user-declared default constructor?

(See also issue 78.)

Additional note (February, 2011):

This issue should be brought up again in light of constexpr constructors and non-static data member initializers.

Notes from the August, 2011 meeting:

If the implicit default constructor initializes all subobjects, no initializer should be required.




1997. Placement new and previous initialization

Section: 8.6  [dcl.init]     Status: drafting     Submitter: Jason Merrill     Date: 2014-09-08

Given the following example,

  #include <new>

  int main() {
    unsigned char buf[sizeof(int)] = {};
    int *ip = new (buf) int;
    return *ip; // 0 or undefined?
  }

Should the preceding initializsation of the buffer carry over to the value of *ip? According to 8.6 [dcl.init] paragraph 12,

When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced (5.18 [expr.ass]).

In this case, no new storage is being obtained for the int object created by the new-expression.




2116. Direct or copy initialization for omitted aggregate initializers

Section: 8.6.1  [dcl.init.aggr]     Status: drafting     Submitter: Richard Smith     Date: 2015-04-22

The Standard does not specify whether the initialization from {} that is done for omitted initializers in aggregate initialization is direct or copy initialization. There is divergence among implementations.

Proposed resolution (May, 2015) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1630.

Notes from the October, 2015 meeting:

CWG agreed that copy initialization should be used; paragraph 7 should have wording similar to paragraph 2. See also issue 1518.




2128. Imprecise rule for reference member initializer

Section: 8.6.1  [dcl.init.aggr]     Status: drafting     Submitter: Richard Smith     Date: 2015-05-19

According to 12.6.2 [class.base.init] paragraph 11,

A temporary expression bound to a reference member from a brace-or-equal-initializer is ill-formed. [Example:

  struct A {
    A() = default;          // OK
    A(int v) : v(v) { }     // OK
    const int& v = 42;      // OK
  };
  A a1;                     // error: ill-formed binding of temporary to reference
  A a2(1);                  // OK, unfortunately

end example]

The rule is intended to apply only if an actual initialization results in such a binding, but it could be read as applying to the declaration of A::v itself. It would be clearer if the restriction were moved into bullet 9.1, e.g.,




2149. Brace elision and array length deduction

Section: 8.6.1  [dcl.init.aggr]     Status: drafting     Submitter: Vinny Romano     Date: 2015-06-25

According to 8.6.1 [dcl.init.aggr] paragraph 4,

An array of unknown size initialized with a brace-enclosed initializer-list containing n initializer-clauses, where n shall be greater than zero, is defined as having n elements (8.3.4 [dcl.array]).

However, the interaction of this with brace elision is not clear. For instance, in the example in paragraph 7,

  struct X { int i, j, k = 42; };
  X a[] = { 1, 2, 3, 4, 5, 6 };
  X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };

a and b are said to have the same value, even though there are six initializer-clauses in the initializer list in a's initializer and two in b's initializer.

Similarly, 14.8.2.1 [temp.deduct.call] paragraph 1 says,

in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list

Should that take into account the underlying type of the array? For example,

  template<int N> void f1(const X(&)[N]);
  f1({ 1, 2, 3, 4, 5, 6 }); // Is N deduced to 2 or 6?

  template<int N> void f2(const X(&)[N][2]);
  f2({ 1, 2, 3, 4, 5, 6 }); // Is N deduced to 1 or 6?



1304. Omitted array bound with string initialization

Section: 8.6.2  [dcl.init.string]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-04-26

The example in 8.6.2 [dcl.init.string] paragraph 1 says,

  char msg[] = "Syntax error on line %s\n";

shows a character array whose members are initialized with a string-literal. Note that because '\n' is a single character and because a trailing '\0' is appended, sizeof(msg) is 25.

However, there appears to be no normative specification of how the size of the array is to be calculated.




1414. Binding an rvalue reference to a reference-unrelated lvalue

Section: 8.6.3  [dcl.init.ref]     Status: drafting     Submitter: Mike Miller     Date: 2011-11-09

Currently an attempt to bind an rvalue reference to a reference-unrelated lvalue succeeds, binding the reference to a temporary initialized from the lvalue by copy-initialization. This appears to be intentional, as the accompanying example contains the lines

    int i3 = 2;
    double&& rrd3 = i3;  // rrd3 refers to temporary with value 2.0

This violates the expectations of some who expect that rvalue references can be initialized only with rvalues. On the other hand, it is parallel with the handling of an lvalue reference-to-const (and is handled by the same wording). It also can add efficiency without requiring existing code to be rewritten: the implicitly-created temporary can be moved from, just as if the call had been rewritten to create a prvalue temporary from the lvalue explicitly.

On a related note, assuming the binding is permitted, the intent of the overload tiebreaker found in 13.3.3.2 [over.ics.rank] paragraph 3 is not clear:

At question is what “to an rvalue” means here. If it is referring to the value category of the initializer itself, before conversions, then the supposed performance advantage of the binding under discussion does not occur because the competing rvalue and lvalue reference overloads will be ambiguous:

    void f(int&&);    // #1
    void f(const int&);
    void g(double d) {
        f(d);         // ambiguous: #1 does not bind to an rvalue
    }

On the other hand, if “to an rvalue” refers to the actual object to which the reference is bound, i.e., to the temporary in the case under discussion, the phrase would seem to be vacuous because an rvalue reference can never bind directly to an lvalue.

Notes from the February, 2012 meeting:

CWG agreed that the binding rules are correct, allowing creation of a temporary when binding an rvalue reference to a non-reference-related lvalue. The phrase “to an rvalue” in 13.3.3.2 [over.ics.rank] paragraph 3 is a leftover from before binding an rvalue reference to an lvalue was prohibited and should be removed. A change is also needed to handle the following case:

    void f(const char (&)[1]);         // #1
    template<typename T> void f(T&&);  // #2
    void g() {
      f("");                           //calls #2, should call #1
    }

Additional note (October, 2012):

Removing “to an rvalue,” as suggested, would have the effect of negating the preference for binding a function lvalue to an lvalue reference instead of an rvalue reference because the case would now fall under the preceding bullet of 13.3.3.2 [over.ics.rank] paragraph 3 bullet 1, sub-bullets 4 and 5:

Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

Presumably if the suggested resolution is adopted, the order of these two bullets should be inverted.




1827. Reference binding with ambiguous conversions

Section: 8.6.3  [dcl.init.ref]     Status: drafting     Submitter: Hubert Tong     Date: 2014-01-07

In the following case,

  struct A {
    operator int &&() const;
    operator int &&() volatile;
    operator long();
  };

  int main() {
    int &&x = A();
  }

the conversion for direct binding cannot be used because of the ambiguity, so indirect binding is used, which allows the use of the conversion to long in creating the temporary.

Is this intended? There is implementation variation.

Notes from the February, 2014 meeting:

CWG agreed that an ambiguity like this should make the initialization ill-formed instead of falling through to do indirect binding.




2018. Qualification conversion vs reference binding

Section: 8.6.3  [dcl.init.ref]     Status: drafting     Submitter: Richard Smith     Date: 2014-10-07

Qualification conversions are not considered when doing reference binding, which leads to some unexpected results:

  template<typename T> T make();
  struct B {}; struct D : B {};

  const int *p1 = make<int*>();           // ok, qualification conversion
  const int *const *p2 = make<int**>();   // ok, qualification conversion
  const int **p3 = make<int**>();         // error, not type safe

  const int &r1 = make<int&>();           // ok, binds directly
  const int *const &r2 = make<int*&>();   // weird, binds to a temporary
  const int *&r3 = make<int*&>();         // error

  const int &&x1 = make<int&&>();         // ok, binds directly
  const int *const &&x2 = make<int*&&>(); // weird, binds to a temporary
  const int *&&x3 = make<int*&&>();       // weird, binds to a temporary

It might make sense to say that similar types are reference-related and if there is a qualification conversion they are reference-compatible.

See also issue 2023.




1996. Reference list-initialization ignores conversion functions

Section: 8.6.4  [dcl.init.list]     Status: drafting     Submitter: Richard Smith     Date: 2014-09-04

The specification for list-initialization of a reference does not consider the existence of conversion functions. Consequently, the following example is ill-formed:

  struct S { operator struct D &(); } s;
  D &d{s};



1803. opaque-enum-declaration as member-declaration

Section: 9.2  [class.mem]     Status: drafting     Submitter: Peter Sommerlad     Date: 2013-10-31

According to 9.2 [class.mem] paragraph 1,

A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

However, the grammar for member-declaration does not have a production that allows an opaque-enum-declaration.




1890. Member type depending on definition of member function

Section: 9.2  [class.mem]     Status: drafting     Submitter: Hubert Tong     Date: 2014-03-07

Consider an example like:

  struct A {
    struct B {
      auto foo() { return 0; }
    };
    decltype(B().foo()) x;
  };

There does not appear to be a prohibition of cases like this, where the type of a member depends on the definition of a member function.

(See also issues 1360 and 1397.)




1983. Inappropriate use of virt-specifier

Section: 9.2  [class.mem]     Status: drafting     Submitter: Richard Smith     Date: 2014-08-11

The restriction in 9.2 [class.mem] paragraph 8 that a virt-specifier may appear only in the declaration of a virtual function is insufficient to rule out examples like the following:

  struct A { virtual void f(); };
  struct B { friend void A::f() final; };

  template<typename T> struct C { virtual void f() {} };
  template void C<int>::f() final;
  template<> void C<char>::f() final;

One possibility might be to require that a virt-specifier appear only on the first declaration of a function.




1283. Static data members of classes with typedef name for linkage purposes

Section: 9.2.3.2  [class.static.data]     Status: drafting     Submitter: Mike Miller     Date: 2011-03-29

According to 9.2.3.2 [class.static.data] paragraph 4,

Unnamed classes and classes contained directly or indirectly within unnamed classes shall not contain static data members.

There is no such restriction on member functions, and there is no rationale for this difference, given that both static data members and member functions can be defined outside a unnamed class with a typedef name for linkage purposes. (Issue 406 acknowledged the lack of rationale by removing the specious note in 9.2.3.2 [class.static.data] that attempted to explain the restriction but left the normative prohibition in place.)

It would be more consistent to remove the restriction for classes with a typedef name for linkage purposes.

Additional note (August, 2012):

It was observed that, since no definition of a const static data member is required if it is not odr-used, there is no reason to prohibit such members in an unnamed class even without a typedef name for linkage purposes.




1721. Diagnosing ODR violations for static data members

Section: 9.2.3.2  [class.static.data]     Status: drafting     Submitter: Mike Miller     Date: 2013-07-31

Describing the handling of static data members with brace-or-equal-initializers, 9.2.3.2 [class.static.data] paragraph 3 says,

The member shall still be defined in a namespace scope if it is odr-used (3.2 [basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer.

The word “shall” implies a required diagnostic, but this is describing an ODR violation (the static data member might be defined in a different translation unit) and thus should be “no diagnostic required.”




1404. Object reallocation in unions

Section: 9.3  [class.union]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-10-19

According to 9.3 [class.union] paragraph 4,

[Note: In general, one must use explicit destructor calls and placement new operators to change the active member of a union. —end note] [Example: Consider an object u of a union type U having non-static data members m of type M and n of type N. If M has a non-trivial destructor and N has a non-trivial constructor (for instance, if they declare or inherit virtual functions), the active member of u can be safely switched from m to n using the destructor and placement new operator as follows:

  u.m.~M();
  new (&u.n)  N;

end example]

This pattern is only “safe” if the original object that is being destroyed does not involve any const-qualified or reference types, i.e., satisfies the requirements of 3.8 [basic.life] paragraph 7, bullet 3:

Although paragraph 4 of 9.3 [class.union] is a note and an example, it should at least refer to the lifetime issues described in 3.8 [basic.life].

Additional note (October, 2013):

See also issue 1776, which suggests possibly changing the restriction in 3.8 [basic.life]. If such a change is made, this issue may become moot.




1702. Rephrasing the definition of “anonymous union”

Section: 9.3  [class.union]     Status: drafting     Submitter: Richard Smith     Date: 2013-06-17

9.3 [class.union] paragraph 5 defines an anonymous union as follows:

A union of the form

is called an anonymous union; it defines an unnamed object of unnamed type.

It is obviously intended that a declaration like

    static union { int i; float f; };

is a declaration of that form (cf paragraph 6, which requires the static keyword for anonymous unions declared in namespace scope). However, it would be clearer if the definition were recast in more descriptive terms, e.g.,

An anonymous union is an unnamed class that is defined with the class-key union in a simple-declaration in which the init-declarator-list is omitted. Such a simple-declaration is treated as if it contained a single declarator declaring an unnamed variable of the union's type.

(Note that this definition would require some additional tweaking to apply to class member anonymous union declarations, since simple-declarations are not included as member-declarations.)

As a related point, it is not clear how the following examples are to be treated, and there is implementation variance on some:

   void f() { thread_local union { int a; }; }
   void g() { extern union { int b; }; }
   thread_local union { int c; }; // static is implied by thread_local
   static thread_local union { int d; };
   static const union { int e = 0; }; // is e const? Clang says yes, gcc says no
   static constexpr union { int f = 0; };



1801. Kind of expression referring to member of anonymous union

Section: 9.3  [class.union]     Status: drafting     Submitter: David Majnemer     Date: 2013-10-24

It is not clear whether naming a member of a global anonymous union should be considered an id-expression or implicitly a member access expression. For example, given

  static union {
    int i;
  };

  template <int &> struct S {};
  S<i> V;

is the last line well-formed? There is implementation variance on this question.

Notes from the February, 2014 meeting:

CWG agreed that the example should be ill-formed.




2080. Example with empty anonymous union member

Section: 9.3  [class.union]     Status: drafting     Submitter: John Spicer     Date: 2015-02-03

The example in 9.3 [class.union] paragraph 8 reads,

  union U {
    int x = 0;
    union { };
    union {
      int z;
      int y = 1; // error: initialization for second variant member of U
    };
  };

The empty anonymous union appears to be a violation of the requirement in 9.2 [class.mem] paragraph 1,

Except when used to declare friends (11.3 [class.friend]), to declare an unnamed bit-field (9.2.4 [class.bit]), or to introduce the name of a member of a base class into a derived class (7.3.3 [namespace.udecl]), or when the declaration is an empty-declaration, member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.



1710. Missing template keyword in class-or-decltype

Section: 10  [class.derived]     Status: drafting     Submitter: Richard Smith     Date: 2013-07-03

A class-or-decltype is used as a base-specifier and as a mem-initializer-id that names a base class. It is specified in 10 [class.derived] paragraph 1 as:

Consequently, a declaration like

  template<typename T> struct D : T::template B<int>::template C<int> {};

is ill-formed, although most implementations accept it; some actually require the use of the template keyword, although the relevant wording in 14.2 [temp.names] paragraph 4 only requires it in a qualified-id, not in a class-or-decltype. It would probably be good to add a production like

to the definition of class-or-decltype and explicitly mention those contexts in 14.2 [temp.names] as not requiring use of the template keyword.

Additional note (January, 2014):

This is effectively issues 314 and 343.

See also issue 1812.

Proposed resolution (February, 2014):

  1. Change 9 [class] paragraph 3 as follows:

  2. If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier class-or-decltype in a base-clause (Clause 10 [class.derived]), the program is ill-formed. Whenever a class-key is followed...
  3. Change the grammar in 10 [class.derived] paragraph 1 as follows:

  4. Delete paragraph 4 and change paragraph 5 of 14.2 [temp.names] as follows, splitting paragraph 5 into two paragraphs and moving the example from paragraph 4 into paragraph 5:

  5. 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 expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [Example: ... —end example]

    A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template or alias template. [Note: The keyword template may not be applied to non-template members of class templates. —end note] The nested-name-specifier (_N4567_.5.1.1 [expr.prim.general]) of

    or a nested-name-specifier directly contained in such a nested-name-specifier (recursively), shall not be of the form

    [Note: That is, a simple-template-id shall not be prefixed by the keyword template in these cases. —end note]

    The keyword template is optional in a typename-specifier (14.6 [temp.res]), elaborated-type-specifier (7.1.7.3 [dcl.type.elab]), using-declaration (7.3.3 [namespace.udecl]), or class-or-decltype (Clause 10 [class.derived]), and in recursively directly-contained nested-name-specifiers thereof. In these contexts, a < token is always assumed to introduce a template-argument-list. [Note: Thus, if the preceding name is not a template-name, the program is ill-formed. —end note] In other contexts, when the name of a member template specialization appears after a nested-name-specifier that denotes a dependent type, but the name is not a member of the current instantiation, the member template name shall be prefixed by the keyword template. Similarly, when the name of a member template specialization appears after . or -> in a postfix-expression (5.2 [expr.post]) and the object expression of the postfix-expression is type-dependent, but the name is not a member of the current instantiation (14.6.2.1 [temp.dep.type]), the member template name shall be prefixed by the keyword template. Otherwise, the name is assumed to name a non-template. [Example:

        <From original paragraph 4>
    

    end example] [Note: As is the case with the typename prefix...

This resolution also resolves issues 314, 343, 1794, and 1812.

Additional note, November, 2014:

Concerns have been expressed over the clarity and organization of the proposed resolution, so the issue has been moved back to "review" status to allow CWG to address these concerns.




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.2.2 [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.2.3 [class.static] paragraph 4).




1699. Does befriending a class befriend its friends?

Section: 11.3  [class.friend]     Status: drafting     Submitter: David Krauss     Date: 2013-06-12

According to 11.3 [class.friend] paragraph 2,

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

A friend declaration is a member-declaration, but it is not clear how far the granting of friendship goes in a friend declaration. For example:

  class c {
    class n {};
    friend struct s;
  };

  struct s {
    friend class c::n;          // #1
    friend c::n g();            // #2
    friend void f() { c::n(); } // #3
  }; 

In particular, if a friend function is defined inside the class definition, as in #3, does its definition have access to the private and protected members of the befriending class? Implementations vary on this point.




472. Casting across protected inheritance

Section: 11.4  [class.protected]     Status: drafting     Submitter: Mike Miller     Date: 16 Jun 2004

Does the restriction in 11.4 [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.4 [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.4 [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.4 [class.protected].

Proposed resolution (April, 2011)

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
        n2p->m = 0;     // error (cf 11.4 [class.protected]) for the same reason
      }
    };

end example]




1883. Protected access to constructors in mem-initializers

Section: 11.4  [class.protected]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2014-02-26

According to 11.4 [class.protected] paragraph 1, except when forming a pointer to member,

All other accesses involve a (possibly implicit) object expression (5.2.5 [expr.ref]).

It is not clear that this is strictly true for the invocation of a base class constructor from a mem-initializer. A wording tweak may be advisable.




1353. Array and variant members and deleted special member functions

Section: 12.1  [class.ctor]     Status: drafting     Submitter: Sean Hunt     Date: 2011-08-16

The specification of when a defaulted special member function is to be defined as deleted sometimes overlooks variant and array members.




1360. constexpr defaulted default constructors

Section: 12.1  [class.ctor]     Status: drafting     Submitter: Richard Smith     Date: 2011-08-16

According to 12.1 [class.ctor] paragraph 6, a defaulted default constructor is constexpr if the corresponding user-written constructor would satisfy the constexpr requirements. However, the requirements apply to the definition of a constructor, and a defaulted constructor is defined only if it is odr-used, leaving it indeterminate at declaration time whether the defaulted constructor is constexpr or not.

(See also issue 1358.)

Additional notes (February, 2013):

As an example of this issue, consider:

  struct S {
    int i = sizeof(S);
  };

You can't determine the value of the initializer, and thus whether the initializer is a constant expression, until the class is complete, but you can't complete the class without declaring the default constructor, and whether that constructor is constexpr or not depends on whether the member initializer is a constant expression.

A similar issue arises with the following example:

  struct A {
    int x = 37;
    struct B { int x = 37; } b;
    B b2[2][3] = { { } };
  };

This introduces an order dependency that is not specified in the current text: determining whether the default constructor of A is constexpr requires first determining the characteristics of the initializer of B::x and whether B::B() is constexpr or not.

The problem is exacerbated with class templates, since the current direction of CWG is to instantiate member initializers only when they are needed (see issue 1396). For a specific example:

  struct S;
  template<class T> struct X {
    int i = T().i;
  };
  unsigned n = sizeof(X<S>); // Error?
  struct S { int i; };

This also affects determining whether a class template specialization is a literal type or not; presumably getting the right answer to that requires instantiating the class and all its nonstatic data member initializers.

See also issues 1397 and 1594.

Notes from the September, 2013 meeting:

This issue should be resolved together with issue 1397.

Proposed resolution (May, 2014):

Change 12.1 [class.ctor] paragraphs 4-5 as follows:

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

An implicitly-declared default constructor is constexpr if:

A default constructor is trivial if it is not user-provided and if:

Otherwise, the default constructor is non-trivial.

A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2 [basic.def.odr]) to create an object of its class type (1.8 [intro.object]) or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2 [class.base.init]) and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5 [dcl.constexpr]), the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its non-static data members shall have been implicitly defined. [Note:...

Additional notes, May, 2014:

The proposed resolution inadvertently allows a defaulted default constructor of a class with virtual bases to be constexpr. It has been updated with a change addressing that oversight and returned to "review" status.

See also issue 1890.




1623. Deleted default union constructor and member initializers

Section: 12.1  [class.ctor]     Status: drafting     Submitter: Vinny Romano     Date: 2013-02-15

According to 12.1 [class.ctor] paragraph 5,

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

Because the presence of a non-static data member initializer is the moral equivalent of a mem-initializer, these rules should probably be modified not to define the generated constructor as deleted when a union member has a non-static data member initializer. (Note the non-normative references in 9.3 [class.union] paragraphs 2-3 and 7.1.7.1 [dcl.type.cv] paragraph 2 that would also need to be updated if this restriction is changed.)

It would also be helpful to add a requirement to 9.3 [class.union] requiring either a non-static data member initializer or a user-provided constructor if all the members of the union have const-qualified types.

On a more general note, why is the default constructor defined as deleted just because a member has a non-trivial default constructor? The union itself doesn't know which member is the active one, and default construction won't initialize any members (assuming no brace-or-equal-initializer). It is up to the “owner” of the union to control the lifetime of the active member (if any), and requiring a user-provided constructor is forcing a design pattern that doesn't make sense. Along the same lines, why is the default destructor defined as deleted just because a member has a non-trivial destructor? I would agree with this restriction if it only applied when the union also has a user-provided constructor.

See also issues 1460, 1562, 1587, and 1621.




1808. Constructor templates vs default constructors

Section: 12.1  [class.ctor]     Status: drafting     Submitter: Richard Smith     Date: 2013-11-12

It is not clear when, if ever, a constructor template can be considered to provide a default constructor. For example:

  struct A {
    template<typename ...T> A(T...); // #1
    A(std::initializer_list<long>);  // #2
  };
  A a{};

According to 8.6.4 [dcl.init.list] paragraph 3, A will be value-initialized if it has a default constructor, and there is implementation divergence whether this example calls #1 or #2.

Similarly, for an example like

  struct B {
    template<typename T=int> B(T = 0);
  };

it is not completely clear whether a default constructor should be implicitly declared or not.

More generally, do utterances in the Standard concerning “constructors” also apply to constructor templates?

Notes from the February, 2014 meeting:

One possibility discussed was that we may need to change places that explicitly refer to a default constructor to use overload resolution, similar to the change that was made a few years ago with regard to copy construction vs “copy constructor.” One additional use of “default constructor” is in determining the triviality of a class, but it might be a good idea to remove the concept of a trivial class altogether. This possibility will be explored.

Notes from the February, 2016 meeting:

CWG reaffirmed the direction from the preceding note and also determined that the presence of a constructor template should suppress implicit declaration of a default constructor.




1299. “Temporary objects” vs “temporary expressions”

Section: 12.2  [class.temporary]     Status: drafting     Submitter: Johannes Schaub     Date: 2011-04-16

The Standard is insufficiently precise in dealing with temporaries. It is not always clear when the term “temporary” is referring to an expression whose result is a prvalue and when it is referring to a temporary object.

(See also issue 1568.)

Proposed resolution (February, 2014):

The resolution is contained in document N3918.

This resolution also resolves issues 1651 and 1893.

Additional note, November, 2014:

Concerns have been raised that the meaning of “corresponding temporary object” is not clear enough in the proposed wording. In addition, N3918 says that it resolves issue 1300, but that issue is 1) marked as a duplicate of issue 914 and 2) the subject of continuing deliberations in EWG. This issue is being returned to "review" status to allow CWG to address these concerns.




1651. Lifetime extension of temporary via reference to subobject

Section: 12.2  [class.temporary]     Status: drafting     Submitter: Richard Smith     Date: 2013-04-15

The resolution of issues 616 and 1213, making the result of a member access or subscript expression applied to a prvalue an xvalue, means that binding a reference to such a subobject of a temporary does not extend the temporary's lifetime. 12.2 [class.temporary] should be revised to ensure that it does.

Proposed resolution (February, 2014): [SUPERSEDED]

This issue is resolved by the resolution of issue 1299.




1697. Lifetime extension and copy elision

Section: 12.2  [class.temporary]     Status: drafting     Submitter: Richard Smith     Date: 2013-06-01

In an example like,

  struct S { ~S(); };
  struct X { X(); X(const X&); };
  struct T { S &&s; X x; };
  void f();
  void g() { T t = T{ {}, {} }; f(); }

it appears that the current wording allows two ways of handling this:

  1. The copy to t in g is not elided. X(const X&) is called, then ~S() is called, then f() is called.

  2. The copy to t in g is elided, so the temporary and t are the same object. Thus, the S object's lifetime is extended to the lifetime of the reference t.s, so first f() is called, then ~S() is called (and X(const X&) is not called).

However, EDG and g++ produce a third behavior: they do not call X(const X&), but they destroy the S() temporary at the end of its full-expression. The current wording does not appear to permit this behavior, but it seems preferable that lifetime extension does not depend on whether copy elision is done.




1726. Declarator operators and conversion function

Section: 12.3.2  [class.conv.fct]     Status: drafting     Submitter: James Widman     Date: 2013-08-02

Presumably the following example is intended to be ill-formed:

  struct A {
    (*operator int*());
  };
  A a;
  int *x = a; // Ok?

It is not clear, however, which rule is supposed to reject such a member-declaration.




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.

Additional note, November, 2014:

Here are some additional examples that should be addressed by the resolution of this issue:

   namespace N {
     template<typename T> struct E {};
     typedef E<int> F;
   }
   namespace M {
     typedef N::F H;
   }
   void g(N::F f) {
     typedef N::F G;
     f.G::~E(); // #1
     f.G::~F(); // #2
     f.G::~G(); // #3
     f.N::F::~E(); // #4
     f.N::F::~F(); // #5
     f.N::F::~G(); // #6
     f.M::H::~E(); // #7
     f.M::H::~F(); // #8
     f.M::H::~G(); // #9
     f.M::H::~H(); // #10
   }



1977. Contradictory results of failed destructor lookup

Section: 12.4  [class.dtor]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 2014-07-21

According to 12.4 [class.dtor] paragraph 12,

At the point of definition of a virtual destructor (including an implicit definition (12.8 [class.copy])), the non-array deallocation function is looked up in the scope of the destructor's class (10.2 [class.member.lookup]), and, if no declaration is found, the function is looked up in the global scope. If the result of this lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function or a function with a deleted definition (8.4 [dcl.fct.def]), the program is ill-formed. [Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression (12.5 [class.free]). —end note]

However, bullet 5.3 of that section says that such a lookup failure causes the destructor to be defined as deleted, rather than making the program ill-formed. It appears that paragraph 12 was overlooked when deleted functions were added to the language. See also 12.5 [class.free] paragraph 7.




2158. Polymorphic behavior during destruction

Section: 12.4  [class.dtor]     Status: drafting     Submitter: Richard Smith     Date: 2015-07-13

Consider the following example:

  #include <stdio.h>
  struct Base {
    Base *p;
    virtual void f() { puts("base"); }
    ~Base() {
      p->f();
    }
  };
  struct Derived : Base {
    Derived() { p = this; }
    void f() { puts("derived"); }
    void g() {
      p->f();
      delete this;
    }
  };
  void h() {
    Derived *p = new Derived;
    p->g();
  }

Should this have defined behavior? On the one hand, the Derived object is in its period of destruction, so the behavior of the p->f() call in the Base destructor should be to call Base::f(). On the other hand, p is a pointer to a Derived object whose lifetime has ended, and the rules in 3.8 [basic.life] don't appear to allow the call. (Calling this->f() from the Base destructor would be OK — the question is whether you can do that for a pointer that used to point to the derived object, or if you can only do it for a pointer that was “created” after the dynamic type of the object changed to be Base.)

If the above is valid, it has severe implications for devirtualization. The purpose of 3.8 [basic.life] paragraph 7 appears to be to allow an implementation to assume that if it will perform two loads of a constant field (for instance, a const member, the implicit pointer for a reference member, or a vptr), and the two loads are performed on the “same pointer value”, then they load the same value.

Should there be a rule for destructors similar to that of 12.1 [class.ctor] paragraph 12?

During the construction of a const object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor's this pointer, the value of the object or subobject thus obtained is unspecified.



255. Placement deallocation functions and lookup ambiguity

Section: 12.5  [class.free]     Status: drafting     Submitter: Mike Miller     Date: 26 Oct 2000

Paragraph 4 of 12.5 [class.free] speaks of looking up a deallocation function. While it is an error if a placement deallocation function alone is found by this lookup, there seems to be an assumption that a placement deallocation function and a usual deallocation function can both be declared in a given class scope without creating an ambiguity. The normal mechanism by which ambiguity is avoided when functions of the same name are declared in the same scope is overload resolution; however, there is no mention of overload resolution in the description of the lookup. In fact, there appears to be nothing in the current wording that handles this case. That is, the following example appears to be ill-formed, according to the current wording:

    struct S {
        void operator delete(void*);
        void operator delete(void*, int);
    };
    void f(S* p) {
        delete p;    // ill-formed: ambiguous operator delete
    }

Suggested resolution (Mike Miller, March 2002):

I think you might get the right effect by replacing the last sentence of 12.5 [class.free] paragraph 4 with something like:

After removing all placement deallocation functions, the result of the lookup shall contain an unambiguous and accessible deallocation function.

Additional notes (October, 2012):

This issue should be reconsidered in list of paper N3396, as it would add additional overloads for allocation and deallocation functions.

The term “usual deallocation function” is defined in 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 2; perhaps it could be used to good effect in 5.3.5 [expr.delete] paragraph 7. The specifications in 12.5 [class.free] paragraphs 4 and 5 should probably also be moved into 5.3.5 [expr.delete].




1621. Member initializers in anonymous unions

Section: 12.6.2  [class.base.init]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2013-02-12

The effect of a non-static data member initializer in an anonymous union is not clearly described in the current wording. Consider the following example:

  struct A {
    struct B {
      union {
        int x = 37;
      };
      union {
        int y = x + 47;  // Well-formed?
      };
    } a;
  }; 

Does an anonymous union have a constructor that applies a non-static data member initializer? Or is the initialization performed by the constructor of the class in which the anonymous union appears? In particular, is the reference to x in the initializer for y well-formed or not? If the initialization of y is performed by B's constructor, there is no problem because B::x is a member of the object being initialized. If an anonymous union has its own constructor, B::x is just a member of the containing class and is a reference to a non-static data member without an object, which is ill-formed. Implementations currently appear to take the latter interpretation and report an error for that initializer.

As a further example, consider:

  union {       // #1
    union {     // #2
      union {   // #3
        int y = 32;
      };
    };
  } a { } ;

One interpretation might be that union #3 has a non-trivial default constructor because of the initializer of y, which would give union #2 a deleted default constructor, which would make the example ill-formed.

As yet another example, consider:

  union {
    union {
      int x;
    };
    union {
      int y = 3;
    };
    union {
      int z;
    };
  } a { };

Assuming the current proposed resolution of issue 1502, what is the correct interpretation of this code? Is it well-formed, and if so, what initialization is performed?

Finally, consider

  struct S {
    union { int x = 1; };
    union { int y = 2; };
  } s{};

Does this violate the prohibition of aggregates containing member initializers in 8.6.1 [dcl.init.aggr] paragraph 1?

See also issues 1460, 1562, 1587, and 1623.




2056. Member function calls in partially-initialized class objects

Section: 12.6.2  [class.base.init]     Status: drafting     Submitter: Richard Smith     Date: 2014-12-11

According to 12.6.2 [class.base.init] paragraph 16,

Member functions (including virtual member functions, 10.3 [class.virtual]) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8 [expr.typeid]) or of a dynamic_cast (5.2.7 [expr.dynamic.cast]). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined.

The example in that paragraph reads, in significant part,

  class B {
  public:
    int f();
  };

  class C {
  public:
    C(int);
  };

  class D : public B, C {
  public:
    D() : C(f())  // undefined: calls member function
                  // but base \tcode{C} not yet initialized
    {}
  };

However, the construction of B, the object for which the member function is being called) has completed its construction, so it is not clear why this should be undefined behavior.

(See also issue 1517.)




1517. Unclear/missing description of behavior during construction/destruction

Section: 12.7  [class.cdtor]     Status: drafting     Submitter: Daniel Krügler     Date: 2012-07-07

The current wording of 12.7 [class.cdtor] paragraph 4 does not describe the behavior of calling a virtual function in a mem-initializer for a base class, only for a non-static data member. Also, the changes for issue 1202 should have been, but were not, applied to the description of the behavior of typeid and dynamic_cast in paragraphs 5 and 6.

In addition, the resolution of issue 597 allowing the out-of-lifetime conversion of pointers/lvalues to non-virtual base classes, should have been, but were not, applied to paragraph 3.

(See also issue 2056.)

Proposed resolution (August, 2013):

  1. Change 12.7 [class.cdtor] paragraph 1 as follows:

  2. For an object with a non-trivial constructor, referring to any non-static member or virtual base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or virtual base class of the object after the destructor finishes execution results in undefined behavior. [Example:
      struct X { int i; };
      struct Y : X { Y(); };                       // non-trivial
      struct A { int a; };
      struct B : public virtual A { int j; Y y; }; // non-trivial
    
      extern B bobj;
      B* pb = &bobj;                               // OK
      int* p1 = &bobj.a;                           // undefined, refers to base class member
      int* p2 = &bobj.y.i;                         // undefined, refers to member's member
    
      A* pa = &bobj;                               // undefined, upcast to a virtual base class type
      B bobj;                                      // definition of bobj
    
      extern X xobj;
      int* p3 = &xobj.i;                           //OK, X is a trivial class
      X xobj;
    
  3. Change 12.7 [class.cdtor] paragraphs 3-6 as follows:

  4. To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect virtual base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from for which B is a direct or indirect virtual base shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct non-static member...

    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 or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class. If the virtual function call uses an explicit class member access (5.2.5 [expr.ref]) and the object expression refers to the complete object of x or one of that object's base class subobjects but not to x or one of its base class subobjects, the behavior is undefined. The period of construction of an object or subobject whose type is a class type C begins immediately after the construction of all its base class subobjects is complete and concludes when the last constructor of class C exits. The period of destruction of an object or subobject whose type is a class type C begins when the destructor for C begins execution and concludes immediately before beginning the destruction of its base class subobjects. A polymorphic operation is a virtual function call (5.2.2 [expr.call]), the typeid operator (5.2.8 [expr.typeid]) when applied to a glvalue of polymorphic type, or the dynamic_cast operator (5.2.7 [expr.dynamic.cast]) when applied to a pointer to or glvalue of a polymorphic type. A polymorphic operand is the object expression in a virtual function call or the operand of a polymorphic typeid or dynamic_cast.

    During the period of construction or period of destruction of an object or subobject whose type is a class type C (call it x), the effect of performing a polymorphic operation in which the polymorphic operand designates x or a base class subobject thereof is as if the dynamic type of the object were class C. [Footnote: This is true even if C is an abstract class, which cannot be the type of a most-derived object. —end footnote] If a polymorphic operand refers to an object or subobject having class type C before its period of construction begins or after its period of destruction is complete, the behavior is undefined. [Note: This includes the evaluation of an expression appearing in a mem-initializer of C in which the mem-initializer-id designates C or one of its base classes. —end note] [Example:

      struct V {
        V();
        V(int);
        virtual void f();
        virtual void g();
      };
    
      struct A : virtual V {
        virtual void f();
        virtual int h();
        A() : V(h()) { }     // undefined behavior: virtual function h called
                             // before A's period of construction begins
      };
    
      struct B : virtual V {
        virtual void g();
        B(V*, A*);
      };
    
      struct D : A, B {
        virtual void f();
        virtual void g();
        D() : B((A*)this, this) { }
      };
    
      B::B(V* v, A* a) {
        f();                 // calls V::f, not A::f
        g();                 // calls B::g, not D::g
        v->g();              // v is base of B, the call is well-defined, calls B::g
        a->f();              // undefined behavior, a's type not a base of B
        typeid(*this);       // type_info for B
        typeid(*v);          // well-defined: *v has type V, a base of B,
                             // so its period of construction is complete;
                             // yields type_info for B
        typeid(*a);          // undefined behavior: A is not a base of B,
                             // so its period of construction has not begun
        dynamic_cast<B*>(v); // well-defined: v has type V*, V is a base of B,
                             // so its period of construction is complete;
                             // results in this
        dynamic_cast<B*>(a); // undefined behavior: A is not a base of B,
                             // so its period of construction has not begun
      }
    

    end example]

    The typeid operator (5.2.8 [expr.typeid]) can be used during construction or destruction (12.6.2 [class.base.init]). When typeid is used in a constructor (including the mem-initializer or brace-or-equal-initializer for a non-static data member) or in a destructor, or used in a function called (directly or indirectly) from a constructor or destructor, if the operand of typeid refers to the object under construction or destruction, typeid yields the std::type_info object representing the constructor or destructor's class. If the operand of typeid refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the result of typeid is undefined.

    dynamic_casts (5.2.7 [expr.dynamic.cast]) can be used during construction or destruction (12.6.2 [class.base.init]). When a dynamic_cast is used in a constructor (including the mem-initializer or brace-or-equal-initializer for a non-static data member) or in a destructor, or used in a function called (directly or indirectly) from a constructor or destructor, if the operand of the dynamic_cast refers to the object under construction or destruction, this object is considered to be a most derived object that has the type of the constructor or destructor's class. If the operand of the dynamic_cast refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, the dynamic_cast results in undefined behavior. [Example:

      struct V {
        virtual void f();
      };
    
      struct A : virtual V { };
    
      struct B : virtual V {
        B(V*, A*);
      };
    
      struct D : A, B {
        D() : B((A*)this, this) { }
      };
    
      B::B(V* v, A* a) {
        typeid(*this);       // type_info for B
        typeid(*v);          // well-defined: *v has type V, a base of B
                             // yields type_info for B
        typeid(*a);          // undefined behavior: type A not a base of B
        dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B
                             // results in B*
        dynamic_cast<B*>(a); // undefined behavior,
                             // a has type A*, A not a base of B
    

    end example]




1092. Cycles in overload resolution during instantiation

Section: 12.8  [class.copy]     Status: drafting     Submitter: Jason Merrill     Date: 2010-07-15

Moving to always doing overload resolution for determining exception specifications and implicit deletion creates some unfortunate cycles:

    template<typename T> struct A {
       T t;
    };

    template <typename T> struct B {
       typename T::U u;
    };

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

    template <typename T> struct D {
       C<B<T> > v;
    };

    struct E {
       typedef A<D<E> > U;
    };

    extern A<D<E> > a;
    A<D<E> > a2(a);

If declaring the copy constructor for A<D<E>> is part of instantiating the class, then we need to do overload resolution on D<E>, and thus C<B<E>>. We consider C(const B<E>&), and therefore look to see if there's a conversion from C<B<E>> to B<E>, which instantiates B<E>, which fails because it has a field of type A<D<E>> which is already being instantiated.

Even if we wait until A<D<E>> is considered complete before finalizing the copy constructor declaration, declaring the copy constructor for B<E> will want to look at the copy constructor for A<D<E>>, so we still have the cycle.

I think that to avoid this cycle we need to short-circuit consideration of C(const T&) somehow. But I don't see how we can do that without breaking

    struct F {
       F(F&);
    };

    struct G;
    struct G2 {
       G2(const G&);
    };

    struct G {
       G(G&&);
       G(const G2&);
    };

    struct H: F, G { };

    extern H h;
    H h2(h);

Here, since G's move constructor suppresses the implicit copy constructor, the defaulted H copy constructor calls G(const G2&) instead. If the move constructor did not suppress the implicit copy constructor, I believe the implicit copy constructor would always be viable, and therefore a better match than a constructor taking a reference to another type.

So perhaps the answer is to reconsider that suppression and then disqualify any constructor taking (a reference to) a type other than the constructor's class from consideration when looking up a subobject constructor in an implicitly defined constructor. (Or assignment operator, presumably.)

Another possibility would be that when we're looking for a conversion from C<B<E>> to B<E> we could somehow avoid considering, or even declaring, the B<E> copy constructor. But that seems a bit dodgy.

Additional note (October, 2010):

An explicitly declared move constructor/op= should not suppress the implicitly declared copy constructor/op=; it should cause it to be deleted instead. This should prevent a member function taking a (reference to) an un-reference-related type from being chosen by overload resolution in a defaulted member function.

And we should clarify that member functions taking un-reference-related types are not even considered during overload resolution in a defaulted member function, to avoid requiring their parameter types to be complete.




1499. Missing case for deleted move assignment operator

Section: 12.8  [class.copy]     Status: drafting     Submitter: John Spicer     Date: 2012-04-27

Bullet 4 of 12.8 [class.copy] paragraph 23 says that a defaulted copy/move assignment operator is defined as deleted if the class has

a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3 [over.match]), as applied to M's corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator

The intent of this is that if overload resolution fails to find a corresponding copy/move assignment operator that can validly be called to copy/move a member, the class's assignment operator will be defined as deleted. However, this wording does not cover an example like the following:

  struct A {
    A();
  };

  struct B {
    B();
    const A a;
  };

  typedef B& (B::*pmf)(B&&);

  pmf p =&B::operator=; 

Here, the problem is simply that overload resolution failed to find a callable function, which is not one of the cases listed in the current wording. A similar problem exists for base classes in the fifth bullet.

Additional note (January, 2013):

A similar omission exists in paragraph 11 for copy constructors.




1548. Copy/move construction and conversion functions

Section: 12.8  [class.copy]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2012-09-02

The current wording of 12.8 [class.copy] paragraph 31 refers only to constructors and destructors:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.

However, in some cases (e.g., auto_ptr) a conversion function is also involved in the copying, and it could presumably also have visible side effects that would be eliminated by copy elision. (Some additional contexts that may also require changes in this regard are mentioned in the resolution of issue 535.)

Additional note (September, 2012):

The default arguments of an elided constructor can also have side effects and should be mentioned, as well; however, the elision should not change the odr-use status of functions and variables appearing in those default arguments.




1590. Bypassing non-copy/move constructor copying

Section: 12.8  [class.copy]     Status: drafting     Submitter: Richard Smith     Date: 2012-11-26

Copy initialization in some cases uses constructors that are not copy/move constructors (e.g., a specialization of a constructor template might be selected by overload resolution, or in copy-list-initialization, any constructor could be selected). Some ABIs require that an object of certain class types be passed in a register (effectively using the trivial copy/move constructor), even if the class has a non-trivial constructor that would be selected to do the copy. The Standard should be changed to permit this usage.

Proposed resolution (April, 2013):

Add the following as a new paragraph following 12.8 [class.copy] paragraph 1:

When an object of class type X is passed to or returned from a function, if X has a trivial, accessible copy or move constructor that is not deleted, and X has no non-trivial copy constructors, move constructors, or destructors, implementations are permitted to perform an additional copy or move of the object using the trivial constructor (even if it would not be selected by overload resolution to perform a copy or move of the object). [Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. —end note]

See also issue 1928.

Additional note, May, 2014:

Questions have been raised regarding this resolution. In particular, the interaction of the “extra copy” with copy elision, lifetime, and access checking context are not specified. In addition, some concern has also been expressed regarding the requirement that the trivial copy/move constructor be accessible. The issue is being returned to "review" status for discussion of these points.

Notes from the June, 2014 meeting:

CWG felt that the requirements for accessibility should be removed, in line with the idea making all access public in a program should not change its semantics. Similarly, the prohibition of non-trivial functions was not desirable. The approach should be to recognize the extra copy as a temporary object and deal explicitly with its lifetime.




1594. Lazy declaration of special members vs overload errors

Section: 12.8  [class.copy]     Status: drafting     Submitter: Richard Smith     Date: 2012-12-06

The implicit declaration of a special member function sometimes requires overload resolution, in order to select a special member to use for base classes and non-static data members. This can be required to determine whether the member is or would be deleted, and whether the member is trivial, for instance. The standard appears to require such overload resolution be performed at the end of the definition of the class, but in practice, implementations perform it lazily. This optimization appears to be non-conforming, in the case where overload resolution would hit an error. In order to enable this optimization, such errors should be “no diagnostic required.”

Additional note (March, 2013):

See also issue 1360.

Notes from the September, 2013 meeting:

The problem with this approach is that hard errors (not in the immediate context) can occur, affecting portability. There are some cases, such as a virtual assignment operator in the base class, where lazy evaluation cannot be done, so it cannot be mandated.




1252. Overloading member function templates based on dependent return type

Section: 13.1  [over.load]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-06

The Standard does not allow overloading of member functions that differ only in their return type (cf enable_if).




1898. Use of “equivalent” in overload resolution

Section: 13.2  [over.dcl]     Status: drafting     Submitter: Hubert Tong     Date: 2014-03-21

The normative text of 13.2 [over.dcl] relies on the term “equivalent,” for which it refers to 13.1 [over.load], but the term appears there only in non-normative text. The resolution of this issue should be coordinated with that of issue 1668.




1278. Incorrect treatment of contrived object

Section: 13.3.1.1.1  [over.call.func]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-27

Footnote 127 of 13.3.1.1.1 [over.call.func] paragraph 3 reads,

An implied object argument must be contrived to correspond to the implicit object parameter attributed to member functions during overload resolution. It is not used in the call to the selected function. Since the member functions all have the same implicit object parameter, the contrived object will not be the cause to select or reject a function.

It is not true that “the member functions all have the same implicit object parameter.” This statement does not take into account member functions brought into the class by using-declarations or cv-qualifiers and ref-qualifiers on the non-static member functions:

    struct B
    {
      char f();         // B &
    };

    struct D : B
    {
      using B::f;
      long f();         // D &

      char g() const;   // D const &
      long g();         // D &

      char h() &;       // D &
      long h() &&;      // D &&
    };

    int main()
    {
      // D::f() has better match than B::f()
      decltype(D().f()) *p1 = (long *)0;

      // D::g() has better match than D::g() const
      decltype(D().g()) *p2 = (long *)0;

      // D::h() & is not viable function
      // D::h() && is viable function
      decltype(D().h()) *p3 = (long *)0;
    }

The value category of a contrived object expression is not specified by the rules and, probably, cannot be properly specified in presence of ref-qualifiers, so the statement “the contrived object will not be the cause to select or reject a function” should be normative rather than informative:

    struct X
    {
      static void f(double) {}
      void f(int) & {}
      void f(int) && {}
    };

    int main()
    {
      X::f(0); // ???
    }



2007. Argument-dependent lookup for operator=

Section: 13.3.1.2  [over.match.oper]     Status: drafting     Submitter: Richard Smith     Date: 2014-09-23

Consider an example like:

  template<typename T> struct A { typename T::error e; };
  template<typename T> struct B { };
  B<A<void>> b1, &b2 = (b1 = b1);

If the assignment operator performs argument-dependent lookup, A<void> will be an associated class and will be instantiated, producing an error. Similar questions apply to the other member-only overloaded operators, operator-> and operator[]. Bullet 3.2 of 13.3.1.2 [over.match.oper] should be changed not to perform unqualified lookup for these operators.




2089. Restricting selection of builtin overloaded operators

Section: 13.3.1.2  [over.match.oper]     Status: drafting     Submitter: Hubert Tong     Date: 2015-02-26

The candidates selected by 13.3.1.2 [over.match.oper] include built-in candidates that will result in an error if chosen; this was affirmed by issue 1687. As a result, t+u is ill-formed because it is resolved to the built-in operator+(int*,std::ptrdiff_t), although most implementations do not (yet) agree:

  struct Adaptor { Adaptor(int); };

  struct List { };
  void operator +(List &, Adaptor);

  struct DataType {
    operator int *() const = delete;
    operator List &() const;
  };

  struct Yea;
  struct Nay { int theNaysHaveIt; };

  template <typename T, typename U>
  Yea addCheck(int, T &&t, U &&u, char (*)[sizeof(t + u, 0)] = 0);

  template <typename T, typename U>
  Nay addCheck(void *, T &&t, U &&u);

  void test(DataType &data) { (void)sizeof(addCheck(0, data,
  0.).theNaysHaveIt); }

It might be better to adjust the candidate list in 13.3.1.3 [over.match.ctor] bullet 3.3.3 to allow conversion only on class types and exclude the second standard conversion sequence.




2028. Converting constructors in rvalue reference initialization

Section: 13.3.1.6  [over.match.ref]     Status: drafting     Submitter: Mitsuru Kariya     Date: 2014-10-25

Consider the following example:

  struct T {
    T() {}
    T(struct S&) {}
  };

  struct S {
    operator T() { return T(); }
  };

  int main()
  {
    S s;
    T&& t(s);  // #1
  }

Because there are two possible conversions from S to T, one by conversion function and the other by converting constructor, one might expect that the initialization at #1 would be ambiguous. However, 13.3.1.6 [over.match.ref] (used in the relevant bullet of 8.6.3 [dcl.init.ref], paragraph 5.2.1.2) only deals with conversion functions and ignores converting constructors.

Notes from the November, 2014 meeting:

CWG agreed that 8.6.3 [dcl.init.ref] should be changed to consider converting constructors in this case.




2108. Conversions to non-class prvalues in reference initialization

Section: 13.3.1.6  [over.match.ref]     Status: drafting     Submitter: Hubert Tong     Date: 2015-03-24

In 13.3.1.6 [over.match.ref], candidates that produce non-class prvalues are considered, although that seems to contradict what 8.6.3 [dcl.init.ref] says. See also issue 2077.




455. Partial ordering and non-deduced arguments

Section: 13.3.3  [over.match.best]     Status: drafting     Submitter: Rani Sharoni     Date: 19 Jan 2004

It's not clear how overloading and partial ordering handle non-deduced pairs of corresponding arguments. For example:

template<typename T>
struct A { typedef char* type; };

template<typename T> char* f1(T, typename A<T>::type);  // #1
template<typename T> long* f1(T*, typename A<T>::type*); // #2

long* p1 = f1(p1, 0); // #3

I thought that #3 is ambiguous but different compilers disagree on that. Comeau C/C++ 4.3.3 (EDG 3.0.3) accepted the code, GCC 3.2 and BCC 5.5 selected #1 while VC7.1+ yields ambiguity.

I intuitively thought that the second pair should prevent overloading from triggering partial ordering since both arguments are non-deduced and has different types - (char*, char**). Just like in the following:

template<typename T> char* f2(T, char*);   // #3
template<typename T> long* f2(T*, char**); // #4

long* p2 = f2(p2, 0); // #5

In this case all the compilers I checked found #5 to be ambiguous. The standard and DR 214 is not clear about how partial ordering handle such cases.

I think that overloading should not trigger partial ordering (in step 13.3.3 [over.match.best]/1/5) if some candidates have non-deduced pairs with different (specialized) types. In this stage the arguments are already adjusted so no need to mention it (i.e. array to pointer). In case that one of the arguments is non-deuced then partial ordering should only consider the type from the specialization:

template<typename T> struct B { typedef T type; };

template<typename T> char* f3(T, T);                   // #7
template<typename T> long* f3(T, typename B<T>::type); // #8

char* p3 = f3(p3, p3); // #9

According to my reasoning #9 should yield ambiguity since second pair is (T, long*). The second type (i.e. long*) was taken from the specialization candidate of #8. EDG and GCC accepted the code. VC and BCC found an ambiguity.

John Spicer: There may (or may not) be an issue concerning whether nondeduced contexts are handled properly in the partial ordering rules. In general, I think nondeduced contexts work, but we should walk through some examples to make sure we think they work properly.

Rani's description of the problem suggests that he believes that partial ordering is done on the specialized types. This is not correct. Partial ordering is done on the templates themselves, independent of type information from the specialization.

Notes from October 2004 meeting:

John Spicer will investigate further to see if any action is required.

(See also issue 885.)




2077. Overload resolution and invalid rvalue-reference initialization

Section: 13.3.3.1.4  [over.ics.ref]     Status: drafting     Submitter: Richard Smith     Date: 2015-01-29

The resolution of issue 1604 broke the following example:

  struct A {};
  struct B { operator const A() const; };
  void f(A const&);
  void f(A&&);

  int main() {
    B a;
    f(a);
  }

Overload resolution selects the A&& overload, but then initialization fails. This seems like a major regression; we're now required to reject

   std::vector<A> va;
   B b;
   va.push_back(b);

Should we update 13.3.3.1.4 [over.ics.ref] to match the changes made to 8.6.3 [dcl.init.ref]?

See also issue 2108.




1536. Overload resolution with temporary from initializer list

Section: 13.3.3.1.5  [over.ics.list]     Status: drafting     Submitter: Mike Miller     Date: 2012-08-14

In determining the implicit conversion sequence for an initializer list argument passed to a reference parameter, the intent is that a temporary of the appropriate type will be created and bound to the reference, as reflected in 13.3.3.1.5 [over.ics.list] paragraph 5:

Otherwise, if the parameter is a reference, see 13.3.3.1.4 [over.ics.ref]. [Note: The rules in this section will apply for initializing the underlying temporary for the reference. —end note]

However, 13.3.3.1.4 [over.ics.ref] deals only with expression arguments, not initializer lists:

When a parameter of reference type binds directly (8.6.3 [dcl.init.ref]) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1 [over.best.ics])... If the parameter binds directly to the result of applying a conversion function to the argument expression, the implicit conversion sequence is a user-defined conversion sequence (13.3.3.1.2 [over.ics.user]), with the second standard conversion sequence either an identity conversion or, if the conversion function returns an entity of a type that is a derived class of the parameter type, a derived-to-base Conversion.

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1 [over.best.ics]. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the underlying type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion.

(Note in particular that the reference binding refers to 8.6.3 [dcl.init.ref], which also does not handle initializer lists, and not to 8.6.4 [dcl.init.list].)

Either 13.3.3.1.4 [over.ics.ref] needs to be revised to handle binding references to initializer list arguments or 13.3.3.1.5 [over.ics.list] paragraph 5 needs to be clearer on how the expression specification is intended to be applied to initializer lists.




1789. Array reference vs array decay in overload resolution

Section: 13.3.3.2  [over.ics.rank]     Status: drafting     Submitter: Faisal Vali     Date: 2013-10-01

The current rules make an example like

  template<class T, size_t N> void foo(T (&)[N]);
  template<class T> void foo(T *t);

  int arr[3]{1, 2, 3};
  foo(arr);

ambiguous, even though the first is an identity match and the second requires an lvalue transformation. Is this desirable?




2110. Overload resolution for base class conversion and reference/non-reference

Section: 13.3.3.2  [over.ics.rank]     Status: drafting     Submitter: Alexander Kulpin     Date: 2015-03-27

There are overload tiebreakers that order reference/nonreference and base/derived conversions, but how they relate is not specified. For example:

  struct A { A(); };
  struct B : A {};
  struct C : B {};

  void f1(B&);
  void f1(A);

  void f2(B);
  void f2(A&);

  int main()
  {
     C v;
     f1(v); // all compilers choose f1(B&)
     f2(v); // all compilers choose f2(B)
  }

The Standard does not appear to specify what happens in this case.




1989. Insufficient restrictions on parameters of postfix operators

Section: 13.5  [over.oper]     Status: drafting     Submitter: Richard Smith     Date: 2014-08-30

According to 13.5.7 [over.inc] paragraph 1,

The user-defined function called operator++ implements the prefix and postfix ++ operator. If this function is a non-static member function with no parameters, or a non-member function with one parameter, it defines the prefix increment operator ++ for objects of that type. If the function is a non-static member function with one parameter (which shall be of type int) or a non-member function with two parameters (the second of which shall be of type int), it defines the postfix increment operator ++ for objects of that type.

According to 13.5 [over.oper] paragraph 8,

Operator functions cannot have more or fewer parameters than the number required for the corresponding operator, as described in the rest of this subclause.

This does not rule out an operator++ with more than two parameters, however, since there is no corresponding operator.

One possibility might be to add a sentence like,

A function named operator++ shall declare either a prefix or postfix increment operator.



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.




1444. Type adjustments of non-type template parameters

Section: 14.1  [temp.param]     Status: drafting     Submitter: Johannes Schaub     Date: 2012-01-15

The type adjustment of template non-type parameters described in 14.1 [temp.param] paragraph 8 appears to be underspecified. For example, implementations vary in their treatment of

  template<typename T, T[T::size]> struct A {};
  int dummy;
  A<int, &dummy> a;

and

  template<typename T, T[1]> struct A;
  template<typename T, T*> struct A {};
  int dummy;
  A<int, &dummy> a;

See also issues 1322 and 1668.




1635. How similar are template default arguments to function default arguments?

Section: 14.1  [temp.param]     Status: drafting     Submitter: Richard Smith     Date: 2013-03-06

Default function arguments are instantiated only when needed. Is the same true of default template arguments? For example, is the following well-formed?

  #include <type_traits>

  template<class T>
  struct X {
    template<class U = typename T::type>
    static void foo(int){}
    static void foo(...){}
  };

  int main(){
    X<std::enable_if<false>>::foo(0);
  }

Also, is the effect on lookup the same? E.g.,

  struct S {
    template<typename T = U> void f();
    struct U {};
  };



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.

Additional note (January, 2014):

See also issue 1710.

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1710.




343. Make template optional in contexts that require a type

Section: 14.2  [temp.names]     Status: drafting     Submitter: Steve Adamczyk     Date: 23 April 2002

By analogy with typename, the keyword template used to indicate that a dependent name will be a template name should be optional in contexts where a type is required, e.g., base class lists. We could also consider member and parameter declarations.

This was suggested by issue 314.

Additional note (January, 2014):

See also issue 1710.

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1710.




1478. template keyword for dependent template template arguments

Section: 14.2  [temp.names]     Status: drafting     Submitter: Johannes Schaub     Date: 2012-03-10

According to 14.2 [temp.names] paragraph 4,

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 expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1 [temp.dep.type]), the member template name must be prefixed by the keyword template.

In other words, the template keyword is only required when forming a template-id. However, current compilers reject an example like:

  template<typename T, template<typename> class U = T::X> struct A;

and require the template keyword before X. Should the rule be amended to require the template keyword in cases like this?




1794. template keyword and alias templates

Section: 14.2  [temp.names]     Status: drafting     Submitter: Jonathan Caves     Date: 2013-10-04

The current wording of 14.2 [temp.names] paragraph 5 is:

A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template.

Presumably this should also allow template before alias templates. For example,

  template<template<typename> class Template>
  struct Internal {
          template<typename Arg>
          using Bind = Template<Arg>;
  };

  template<template<typename> class Template, typename Arg>
  using Instantiate = Template<Arg>;

  template<template<typename> class Template, typename Argument>
  using Bind = Instantiate<Internal<Template>::template Bind, Argument>;

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1710.




1812. Omission of template in a typename-specifier

Section: 14.2  [temp.names]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2013-11-18

According to 14.2 [temp.names] paragraph 4,

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 expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

This does not seem necessary in a typename-specifier; a < following a qualified-id in a typename-specifier could safely be assumed to begin a template argument list, so the template keyword should be optional in this case. Some implementations already do not enforce this requirement.

See also issue 1710.

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1710.




2043. Generalized template arguments and array-to-pointer decay

Section: 14.3.2  [temp.arg.nontype]     Status: drafting     Submitter: Richard Smith     Date: 2014-11-13

According to 14.3.2 [temp.arg.nontype] paragraph 1 (newly revised by the adoption of paper N4268),

For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

This change breaks an example like

   template<int *p> struct X {};
   int arr[32];
   X<arr> x;

because the array-to-pointer decay produces a pointer to the first element, which is a subobject.

Suggested resolution:

Change the referenced bullet to read:

Note that this resolution also allows an example like

    template<char &p> struct S { };
    char arr[2];
    S<arr[0]> s_arr;

which may not be exactly what we want.




2049. List initializer in non-type template default argument

Section: 14.3.2  [temp.arg.nontype]     Status: drafting     Submitter: Ville Voutilainen     Date: 2014-11-20

According to 14.3.2 [temp.arg.nontype] paragraph 1,

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20 [expr.const]) of the type of the template-parameter.

This does not permit an example like:

  template <int* x = {}> struct X {};

which seems inconsistent.




2057. Template template arguments with default arguments

Section: 14.3.3  [temp.arg.template]     Status: drafting     Submitter: Jonathan Caves     Date: 2014-12-12

It is not clear how to handle an example like:

  template<typename T1, typename T2 = char> class A { };

  template<template<typename... T> class X> class S {
    X<int> x;
  };

  S<A> a;

Issue 184 dealt with a similar question but did so in the era before variadic templates. This usage should be permitted in modern C++.

Notes from the February, 2016 meeting:

CWG felt that this usage should be permitted, but only for template template parameters with a parameter pack.. Furthermore, if the template template parameter has a default argument followed by a parameter pack, the parameter's default argument would be used, followed by any remaining default arguments from the template template argument.




2037. Alias templates and template declaration matching

Section: 14.4  [temp.type]     Status: drafting     Submitter: Richard Smith     Date: 2014-11-06

For the following example,

  template<int N> struct A {};
  template<short N> using B = A<N>;
  template<int N> void f(B<N>) {} // #1
  template<int N> void f(A<N>) {} // #2

There is implementation variance as to whether there is one f or two. As with previously-discussed cases, these have different SFINAE effects, perhaps equivalent but not functionally equivalent. Should the argument to #1 be treated as something like A<(int)(short)N> and not just A<N>.

See also issues 1668 and 1979.




1729. Matching declarations and definitions of variable templates

Section: 14.5  [temp.decls]     Status: drafting     Submitter: Larisse Voufo     Date: 2013-08-05

The relationship between declarations and definitions of variable templates is not clear. For example:

  template<typename T> auto var0 = T();  // #1a.
  template<typename T> extern T var0;    // #1b.

  template<typename T> T var1;           // #2a.
  template<typename T> extern auto var1; // #2b.
  template<typename T> extern T var1;    // #2c.
  template<typename T> T var1;           // #2d.

Questions:

  1. When is a variable template declaration a definition and when a non-defining declaration?

  2. What declarations are valid?

  3. Should auto declarations be allowed?

  4. To what extent, if any, do these involve type matching?

  5. How are types matched, especially in the presence of auto?




1730. Can a variable template have an unnamed type?

Section: 14.5  [temp.decls]     Status: drafting     Submitter: Larisse Voufo     Date: 2013-08-05

Is it permitted for a variable template to have an unnamed type?




2062. Class template redeclaration requirements

Section: 14.5.1  [temp.class]     Status: drafting     Submitter: Hubert Tong     Date: 2014-12-19

There does not appear to be a rule that two declarations of a class template must have compatible template parameter lists; e.g., it is not clear what makes the following ill-formed:

  template <typename> struct A;
  template <unsigned> struct A;



1432. Newly-ambiguous variadic template expansions

Section: 14.5.3  [temp.variadic]     Status: drafting     Submitter: Daniel Krügler     Date: 2011-12-17

With the new core rules in regard to variadic pack expansions the library specification of the traits template common_type is now broken, the reason is that it is defined as a series of specializations of the primary template

    template <class ...T>
    struct common_type;

The broken one is this pair:

  template <class T, class U>
  struct common_type<T, U> {
   typedef decltype(true ? declval<T>() : declval<U>()) type;
  };

  template <class T, class U, class... V>
  struct common_type<T, U, V...> {
   typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
  };

With the new rules both specializations would now be ambiguous for an instantiation like common_type<X, Y>.

(See also issue 1395.)

Notes from the October, 2012 meeting:

It is possible that 14.5.5.2 [temp.class.order] may resolve this problem.




1545. friend function templates defined in class templates

Section: 14.5.4  [temp.friend]     Status: drafting     Submitter: Daniel Krügler     Date: 2012-08-25

The status of an example like the following is not clear:

   template<class> struct x {
     template<class T>
     friend bool operator==(x<T>, x<T>) { return false; }
  };

  int main() {
    x<int> x1;
    x<double> x2;
    x1 == x1;
    x2 == x2;
  }

Such a friend definition is permitted by 14.5.4 [temp.friend] paragraph 2:

A friend function template may be defined within a class or class template...

Paragraph 4 appears to be related, but deals only with friend functions, not friend function templates:

When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used. The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.



1862. Determining “corresponding members” for friendship

Section: 14.5.4  [temp.friend]     Status: drafting     Submitter: Richard Smith     Date: 2014-02-13

During the discussion of issue 1804, it was noted that the process of determining whether a member of an explicit or partial specialization corresponds to a member of the primary template is not well specified. In particular, it should be clarified that the primary template should not be instantiated during this process; instead, the template arguments from the specialization should simply be substituted into the member declaration.




1647. Type agreement of non-type template arguments in partial specializations

Section: 14.5.5  [temp.class.spec]     Status: drafting     Submitter: John Spicer     Date: 2013-04-04

The Standard appears to be silent on whether the types of non-type template arguments in a partial specialization must be the same as those of the primary template or whether conversions are permitted. For example,

  template<char...> struct char_values {};
  template<int C1, char C3>
  struct char_values<C1, 12, C3> {
    static const unsigned value = 1;
  };
  int check0[char_values<1, 12, 3>::value == 1? 1 : -1]; 

The closest the current wording comes to dealing with this question is 14.5.5 [temp.class.spec] paragraph 8 bullet 1:

In this example, one might think of the first template argument in the partial specialization as (char)C1, which would violate the requirement, but that reasoning is tenuous.

It would be reasonable to require the types to match in cases like this. If this kind of usage is allowed it could get messy if the primary template were int... and the partial specialization had a parameter that was char because not all of the possible values from the primary template could be represented in the parameter of the partial specialization. A similar issue exists if the primary template takes signed char and the partial specialization takes unsigned int.

There is implementation variance in the treatment of this example.

(See also issues 1315, 2033, and 2127.)




1711. Missing specification of variable template partial specializations

Section: 14.5.5  [temp.class.spec]     Status: drafting     Submitter: Richard Smith     Date: 2013-07-08

It appears that partial specializations of variable templates are intended to be supported, as 14.3.3 [temp.arg.template] paragraph 2 says,

Any partial specializations (14.5.5 [temp.class.spec]) associated with the primary class template or primary variable template are considered when a specialization based on the template template-parameter is instantiated.

However, there is no explicit specification for how they are to be handled, and the wording in 14.5.5 [temp.class.spec] and its subsections explicitly applies only to partial specializations of class templates.




2127. Partial specialization and nullptr

Section: 14.5.5  [temp.class.spec]     Status: drafting     Submitter: Faisal Vali     Date: 2015-05-18

An example like the following would seem to be plausible:

  template<class T, T*> struct X { };
  // We want to partially specialize for all nullptrs...
  template<class T> struct X<T, nullptr> { ... }; // NOT OK

This is disallowed by the rule in bullet 8.2 of 14.5.5 [temp.class.spec]:

(See also issues 1315, 1647, and 2033.)




2179. Required diagnostic for partial specialization after first use

Section: 14.5.5  [temp.class.spec]     Status: drafting     Submitter: John Spicer     Date: 2015-10-12

According to 14.5.5 [temp.class.spec] paragraph 1,

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

There are two problems with this wording. First, the “no diagnostic required” provision is presumably to avoid mandating cross-translation-unit analysis, but there is no reason not to require the diagnostic if the rule is violated within a single translation unit. Also, “would make use” is imprecise; it could be interpreted as applying only when the partial specialization would have been selected by a previous specialization, but it should also apply to cases where the partial specialization would have made a previous specialization ambiguous.

Making these two changes would guarantee that a diagnostic is issued for the following example:

   template <class T1, class T2> class A;
   template <class T> struct A<T, void> { void f(); };
   template <class T> void g(T) { A<char, void>().f(); }   // #1
   template<typename T> struct A<char, T> {};
   A<char, void> f;   // #2

It is unspecified whether the reference to A<char, void> at #1 is the “first use” or not. If so, A<char, void> is bound to the first partial specialization and, under the current wording, an implementation is not required to diagnose the ambiguity resulting from the second partial specialization. If #2 is the “first use,” it is clearly ambiguous and must result in a diagnostic. There is implementation divergence on the handling of this example that would be addressed by the suggested changes.




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?

(See also issue 1246.)

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.




1755. Out-of-class partial specializations of member templates

Section: 14.5.5.3  [temp.class.spec.mfunc]     Status: drafting     Submitter: Richard Smith     Date: 2013-09-19

According to 14.5.5.3 [temp.class.spec.mfunc] paragraph 2,

If a member template of a class template is partially specialized, the member template partial specializations are member templates of the enclosing class template; if the enclosing class template is instantiated (14.7.1 [temp.inst], 14.7.2 [temp.explicit]), a declaration for every member template partial specialization is also instantiated as part of creating the members of the class template specialization.

Does this imply that only partial specializations of member templates that are declared before the enclosing class is instantiated are considered? For example, in

  template<typename A> struct X { template<typename B> struct Y; };
  template struct X<int>;
  template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
  int k = X<int>::Y<int*>().n;

is the last line valid? There is implementation variance on this point. Similarly, for an example like

  template<typename A> struct Outer {
   template<typename B, typename C> struct Inner;
  };
  Outer<int> outer;
  template<typename A> template<typename B>
    struct Outer<A>::Inner<typename A::error, B> {};

at what point, if at all, is the declaration of the partial specialization instantiated? Again, there is implementation variance in the treatment of this example.

Notes from the February, 2014 meeting:

CWG decided that partial specialization declarations should be instantiated only when needed to determine whether the partial specialization matches or not.

Additional note, November, 2014:

See also paper N4090.




2045. “Identical” template parameter lists

Section: 14.5.6.1  [temp.over.link]     Status: drafting     Submitter: Faisal Vali     Date: 2014-11-14

The requirement of 14.5.6.1 [temp.over.link] paragraph 6 that equivalent function templates must have “identical” template parameter lists is confusing, since the names of template parameters are not considered (14.5.6.1 [temp.over.link] paragraph 3).




1286. Equivalence of alias templates

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 2011-04-03

Issue 1244 was resolved by changing the example in 14.4 [temp.type] paragraph 1 from

  template<template<class> class TT> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y> y;
  X<Z> z;

to

  template<class T> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y<int> > y;
  X<Z<int> > z;

In fact, the original intent was that the example should have been correct as written; however, the normative wording to make it so was missing. The current wording of 14.5.7 [temp.alias] deals only with the equivalence of a specialization of an alias template with the type-id after substitution. Wording needs to be added specifying under what circumstances an alias template itself is equivalent to a class template.

Proposed resolution (September, 2012):

  1. Add the following as a new paragraph following 14.5.7 [temp.alias] paragraph 2:

  2. When the type-id in the declaration of alias template (call it A) consists of a simple-template-id in which the template-argument-list consists of a list of identifiers naming each template-parameter of A exactly once in the same order in which they appear in A's template-parameter-list, the alias template is equivalent to the template named in the simple-template-id (call it T) if A and T have the same number of template-parameters. [Footnote: This rule is transitive: if an alias template A is equivalent to another alias template B that is equivalent to a class template C, then A is also equivalent to C, and A and B are also equivalent to each other. —end footnote] [Example:

      template<typename T, U = T> struct A;
    
      template<typename V, typename W>
        using B = A<V, W>;                // equivalent to A
    
      template<typename V, typename W>
        using C = A<V>;                   // not equivalent to A:
                                          // not all parameters used
    
      template<typename V>
        using D = A<V>;                   // not equivalent to A:
                                          // different number of parameters
    
      template<typename V, typename W>
        using E = A<W, V>;                // not equivalent to A:
                                          // template-arguments in wrong order
    
      template<typename V, typename W = int>
        using F = A<V, W>;                // equivalent to A:
                                          // default arguments not considered
    
      template<typename V, typename W>
        using G = A<V, W>;                // equivalent to A and B
    
      template<typename V, typename W>
        using H = E<V, W>;                // equivalent to E
    
      template<typename V, typename W>
        using I = A<V, typename W::type>; // not equivalent to A:
                                          // argument not identifier
    
    

    end example]

  3. Change 14.4 [temp.type] paragraph 1 as follows:

  4. Two template-ids refer to the same class or function if

    [Example:

    ...declares x2 and x3 to be of the same type. Their type differs from the types of x1 and x4.

      template<class T template<class> class TT> struct X { };
      template<class> struct Y { };
      template<class T> using Z = Y<T>;
      X<Y<int> Y> y;
      X<Z<int> Z> z;
    

    declares y and z to be of the same type. —end example]

Additional note, November, 2014:

Concern has been expressed over the proposed resolution with regard to its handling of default template arguments that differ between the template and its alias, e.g.,

   template<typename T, typename U = int> struct A {};
   template<typename T, typename U = char> using B = A<T, U>;
   template<template<typename...> typename C> struct X { C<int> c; };

Notes from the May, 2015 meeting:

See also issue 1979, which CWG is suggesting to be resolved by defining a “simple” alias, one in which the SFINAE conditions are the same as the referenced template and that uses all template parameters.




1430. Pack expansion into fixed alias template parameter list

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Jason Merrill     Date: 2011-12-13

Originally, a pack expansion could not expand into a fixed-length template parameter list, but this was changed in N2555. This works fine for most templates, but causes issues with alias templates.

In most cases, an alias template is transparent; when it's used in a template we can just substitute in the dependent template arguments. But this doesn't work if the template-id uses a pack expansion for non-variadic parameters. For example:

    template<class T, class U, class V>
    struct S {};

    template<class T, class V>
    using A = S<T, int, V>;

    template<class... Ts>
    void foo(A<Ts...>);

There is no way to express A<Ts...> in terms of S, so we need to hold onto the A until we have the Ts to substitute in, and therefore it needs to be handled in mangling.

Currently, EDG and Clang reject this testcase, complaining about too few template arguments for A. G++ did as well, but I thought that was a bug. However, on the ABI list John Spicer argued that it should be rejected.

(See also issue 1558.)

Notes from the October, 2012 meeting:

The consensus of CWG was that this usage should be prohibited, disallowing use of an alias template when a dependent argument can't simply be substituted directly into the type-id.

Additional note, April, 2013:

For another example, consider:

  template<class... x> class list{};
  template<class a, class... b> using tail=list<b...>;
  template <class...T> void f(tail<T...>);

  int main() {
    f<int,int>({});
  }

There is implementation variance in the handling of this example.




1554. Access and alias templates

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Jason Merrill     Date: 2012-09-17

The interaction of alias templates and access control is not clear from the current wording of 14.5.7 [temp.alias]. For example:

  template <class T> using foo = typename T::foo;

  class B {
    typedef int foo;
    friend struct C;
  };

  struct C {
    foo<B> f;    // Well-formed?
  };

Is the substitution of B::foo for foo<B> done in the context of the befriended class C, making the reference well-formed, or is the access determined independently of the context in which the alias template specialization appears?

If the answer to this question is that the access is determined independently from the context, care must be taken to ensure that an access failure is still considered to be “in the immediate context of the function type” (14.8.2 [temp.deduct] paragraph 8) so that it results in a deduction failure rather than a hard error.

Notes from the October, 2012 meeting:

The consensus of CWG was that instantiation (lookup and access) for alias templates should be as for other templates, in the definition context rather than in the context where they are used. They should still be expanded immediately, however.

Additional note (February, 2014):

A related problem is raised by the definition of std::enable_if_t (20.15.2 [meta.type.synop]):

  template <bool b, class T = void>
  using enable_if_t = typename enable_if<b,T>::type;

If b is false, there will be no type member. The intent is that such a substitution failure is to be considered as being “in the immediate context” where the alias template specialization is used, but the existing wording does not seem to accomplish that goal.

Additional note, November, 2014:

Concern has been expressed that the intent to analyze access in the context of the alias template definition is at odds with the fact that friendship cannot be granted to alias templates; if it could, the access violation in the original example could be avoided by making foo a friend of class B, but that is not possible.

Additional node, February, 2016:

The issue has been returned to "open" status to facilitate further discussion by CWG as to whether the direction in the October, 2012 note is still desirable.

Notes from the February, 2016 meeting:

CWG reaffirmed the direction described in the October, 2012 note above. With regard to the November, 2014 note regarding granting of friendship, it was observed that the same problem occurs with enumerators, which might refer to inaccessible names in the enumerator volue. The solution in both cases is to embed the declaration in a class and grant the class friendship. See issue 1844, dealing with the definition of “immediate context.”




1896. Repeated alias templates

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Mike Miller     Date: 2014-03-18

The current wording of the Standard does not permit repeated alias template declarations within a scope, but some current implementations allow it, presumably by analogy with typedef declarations. Should the Standard be changed to permit this usage?

Notes from the November, 2014 meeting:

CWG agreed that the usage should be permitted, provided that the dependent types are equivalent. Note that this is a weaker requirement than the token-for-token identity of the ODR, since alias templates are not definitions per 14 [temp] paragraph 1.




1979. Alias template specialization in template member definition

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 2014-07-31

In an example like

  template<typename T> struct A {
    struct B {
      void f();
    };
  };

  template<typename T> using X = typename A<T>::B;

  template<typename T> void X<T>::f() { }       // #1

should #1 be considered a definition of A<T>::B::f()?

Analogy with alias-declarations would suggest that it should, but alias template specializations involve issues like SFINAE on unused template parameters (see issue 1558) and possibly other complications.

(See also issues 1980, 2021, 2025, and 2037.)

Notes from the May, 2015 meeting:

CWG felt that this kind of usage should be permitted only via a “simple” alias, in which the SFINAE is the same as the template to which it refers and all the template parameters are used. See also issue 1286.




1980. Equivalent but not functionally-equivalent redeclarations

Section: 14.5.7  [temp.alias]     Status: drafting     Submitter: Richard Smith     Date: 2014-08-04

In an example like

  template<typename T, typename U> using X = T;
  template<typename T> X<void, typename T::type> f();
  template<typename T> X<void, typename T::other> f();

it appears that the second declaration of f is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.

Notes from the November, 2014 meeting:

CWG felt that these two declarations should not be equivalent.




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?




1271. Imprecise wording regarding dependent types

Section: 14.6  [temp.res]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 2011-03-24

According to 14.6 [temp.res] paragraph 3,

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1 [temp.dep.type]) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill- formed.

The intent of the programmer cannot form the basis for a compiler determining whether to issue a diagnostic or not.

Suggested resolution:

Let N be a qualified-id with a nested-name-specifier that denotes a dependent type. If N is not prefixed by the keyword typename, N shall refer to a member of the current instantiation or it shall not refer to a type.

If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

(See also issues 590 and 591.)

Notes from the November, 2016 meeting:

The resolution for this issue should describe the type to which a typename-specifier refers, effectively the type named by the corresponding simple-type-specifier with typename removed.




1785. Conflicting diagnostic requirements for template definitions

Section: 14.6  [temp.res]     Status: drafting     Submitter: CWG     Date: 2013-09-28

According to 14.6 [temp.res] paragraph 8,

No diagnostic shall be issued for a template for which a valid specialization can be generated.

One sentence later, it says,

If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

This appears to be a contradiction: in the latter case, there is postulated to exist a “valid” specialization (with an empty pack expansion), for which a diagnostic might or might not be issued. The first quoted sentence, however, forbids issuing a diagnostic for a template that has at least one valid specialization.




1841. < following template injected-class-name

Section: 14.6.1  [temp.local]     Status: drafting     Submitter: Ismail Pazarbasi     Date: 2014-01-23

According to 14.6.1 [temp.local]paragraph 1,

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9 [class]). The injected-class-name can be used as a template-name or a type-name. 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 class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

The intent is that a < following such an injected-class-name is to be interpreted as the start of a template-argument-list (and an error if the following tokens do not constitute a valid template-argument-list), but that is not said explicitly.




1936. Dependent qualified-ids

Section: 14.6.2  [temp.dep]     Status: drafting     Submitter: Richard Smith     Date: 2014-06-05

The resolution of issue 1321 changed the term “dependent name” to apply only to unqualified-ids, presumably on the basis that only unqualified-ids affect the lookup set. However, the rule from 14.5.6.1 [temp.over.link] paragraph 5,

For determining whether two dependent names (14.6.2 [temp.dep]) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template. If multiple declarations of the same function template differ in the result of this name lookup, the result for the first declaration is used.

should apply to non-dependent qualified-ids naming functions called with dependent arguments, as well.

There should also be a statement that the name of a member of an unknown specialization is a dependent name and so should fall under the rules of 14.6.4 [temp.dep.res] and not 14.6.3 [temp.nondep].




1390. Dependency of alias template specializations

Section: 14.6.2.1  [temp.dep.type]     Status: drafting     Submitter: Johannes Schaub     Date: 2011-09-04

According to 14.6.2.1 [temp.dep.type] paragraph 8, a type is dependent (among other things) if it is

This applies to alias template specializations, even if the resulting type does not depend on the template argument:

    struct B { typedef int type; };
    template<typename> using foo = B;
    template<typename T> void f() {
      foo<T>::type * x;  //error: typename required
    }

Is a change to the rules for cases like this warranted?

Notes from the October, 2012 meeting:

CWG agreed that no typename should be required in this case. In some ways, an alias template specialization is like the current instantiation and can be known at template definition time.




1524. Incompletely-defined class template base

Section: 14.6.2.1  [temp.dep.type]     Status: drafting     Submitter: Jason Merrill     Date: 2012-07-17

The correct handling of an example like the following is unclear:

  template<typename T> struct A {
    struct B: A { };
  };

A type used as a base must be complete (10 [class.derived] paragraph 2). The fact that the base class in this example is the current instantiation could be interpreted as indicating that it should be available for lookup, and thus the normal rule should apply, as members declared after the nested class would not be visible.

On the other hand, 14.6.2 [temp.dep] paragraph 3 says,

In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

This wording refers not to a dependent type, which would permit lookup in the current instantiation, but simply to a type that “depends on a template-parameter,” and the current instantiation is such a type.

Implementations vary on the handling of this example.

(See also issue 1526 for another case related to the distinction between a “dependent type” and a “type that depends on a template-parameter.”)

Notes from the October, 2012 meeting:

CWG determined that the example should be ill-formed.




2065. Current instantiation of a partial specialization

Section: 14.6.2.1  [temp.dep.type]     Status: drafting     Submitter: Richard Smith     Date: 2014-12-29

According to 14.6.2.1 [temp.dep.type] paragraph 1, a name refers to the current instantiation if it is

in the definition of a partial specialization or a member of a partial specialization, the name of the class template followed by the template argument list of the partial specialization enclosed in <> (or an equivalent template alias specialization).

I don't think this works. How are the argument lists compared? If it's using the “equivalent” rules, this doesn't work because we make no provision for “functionally equivalent but not equivalent” here. If it's using 14.4 [temp.type] paragraph 1, that fails because it doesn't handle dependent template arguments at all.

The same issue would come up when defining members of a partial specialization out-of-line.




2074. Type-dependence of local class of function template

Section: 14.6.2.1  [temp.dep.type]     Status: drafting     Submitter: Richard Smith     Date: 2015-01-20

According to 14.6.2.1 [temp.dep.type] paragraph 9, a local class in a function template is dependent if and only if it contains a subobject of a dependent type. However, given an example like

  template<typename T> void f() {
    struct X {
      typedef int type;
  #ifdef DEPENDENT
      T x;
  #endif
    };
  X::type y;    // #1
  }
  void g() { f<int>(); }

there is implementation variance in the treatment of #1, but whether or not DEPENDENT is defined appears to make no difference.

In a related question, should a value-dependent alignas specifier cause a type to be dependent? Given

  template<int N> struct Y { typedef int type; };
  template<int N> void h() {
    struct alignas(N) X {};
    Y<alignof(X)>::type z;   // #2
  }
  void i() { h<4>(); }

Most/all implementations issue an error for a missing typename in #2.

Perhaps the right answer is that the types should be dependent but a member of the current instantiation, permitting name lookup without typename.




2090. Dependency via non-dependent base class

Section: 14.6.2.4  [temp.dep.temp]     Status: drafting     Submitter: Maxim Kartashev     Date: 2015-02-27

According to 14.6.2.4 [temp.dep.temp] paragraph 3,

a non-type template-argument is dependent if 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.

Members of non-dependent base classes are members of the current instantiation, but using one as a non-type template argument should not be considered dependent.




2. How can dependent names be used in member declarations that appear outside of the class template definition?

Section: 14.6.4  [temp.dep.res]     Status: drafting     Submitter: unknown     Date: unknown
    template <class T> class Foo {
    
       public:
       typedef int Bar;
       Bar f();
    };
    template <class T> typename Foo<T>::Bar Foo<T>::f() { return 1;}
                       --------------------
In the class template definition, the declaration of the member function is interpreted as:
   int Foo<T>::f();
In the definition of the member function that appears outside of the class template, the return type is not known until the member function is instantiated. Must the return type of the member function be known when this out-of-line definition is seen (in which case the definition above is ill-formed)? Or is it OK to wait until the member function is instantiated to see if the type of the return type matches the return type in the class template definition (in which case the definition above is well-formed)?

Suggested resolution: (John Spicer)

My opinion (which I think matches several posted on the reflector recently) is that the out-of-class definition must match the declaration in the template. In your example they do match, so it is well formed.

I've added some additional cases that illustrate cases that I think either are allowed or should be allowed, and some cases that I don't think are allowed.

    template <class T> class A { typedef int X; };
    
    
    template <class T> class Foo {
     public:
       typedef int Bar;
       typedef typename A<T>::X X;
       Bar f();
       Bar g1();
       int g2();
       X h();
       X i();
       int j();
     };
    
     // Declarations that are okay
     template <class T> typename Foo<T>::Bar Foo<T>::f()
                                                     { return 1;}
     template <class T> typename Foo<T>::Bar Foo<T>::g1()
                                                     { return 1;}
     template <class T> int Foo<T>::g2() { return 1;}
     template <class T> typename Foo<T>::X Foo<T>::h() { return 1;}
    
     // Declarations that are not okay
     template <class T> int Foo<T>::i() { return 1;}
     template <class T> typename Foo<T>::X Foo<T>::j() { return 1;}
In general, if you can match the declarations up using only information from the template, then the declaration is valid.

Declarations like Foo::i and Foo::j are invalid because for a given instance of A<T>, A<T>::X may not actually be int if the class is specialized.

This is not a problem for Foo::g1 and Foo::g2 because for any instance of Foo<T> that is generated from the template you know that Bar will always be int. If an instance of Foo is specialized, the template member definitions are not used so it doesn't matter whether a specialization defines Bar as int or not.




287. Order dependencies in template instantiation

Section: 14.6.4.1  [temp.point]     Status: drafting     Submitter: Martin Sebor     Date: 17 May 2001

Implementations differ in their treatment of the following code:

    template <class T>
    struct A {
	typename T::X x;
    };

    template <class T>
    struct B {
	typedef T* X;
	A<B> a;
    };

    int main ()
    {
	B<int> b;
    }

Some implementations accept it. At least one rejects it because the instantiation of A<B<int> > requires that B<int> be complete, and it is not at the point at which A<B<int> > is being instantiated.

Erwin Unruh:

In my view the programm is ill-formed. My reasoning:

So each class needs the other to be complete.

The problem can be seen much easier if you replace the typedef with

    typedef T (*X) [sizeof(B::a)];

Now you have a true recursion. The compiler cannot easily distinguish between a true recursion and a potential recursion.

John Spicer:

Using a class to form a qualified name does not require the class to be complete, it only requires that the named member already have been declared. In other words, this kind of usage is permitted:

    class A {
        typedef int B;
        A::B ab;
    };

In the same way, once B has been declared in A, it is also visible to any template that uses A through a template parameter.

The standard could be more clear in this regard, but there are two notes that make this point. Both 3.4.3.1 [class.qual] and _N4567_.5.1.1 [expr.prim.general] paragraph 7 contain a note that says "a class member can be referred to using a qualified-id at any point in its potential scope (3.3.7 [basic.scope.class])." A member's potential scope begins at its point of declaration.

In other words, a class has three states: incomplete, being completed, and complete. The standard permits a qualified name to be used once a name has been declared. The quotation of the notes about the potential scope was intended to support that.

So, in the original example, class A does not require the type of T to be complete, only that it have already declared a member X.

Bill Gibbons:

The template and non-template cases are different. In the non-template case the order in which the members become declared is clear. In the template case the members of the instantiation are conceptually all created at the same time. The standard does not say anything about trying to mimic the non-template case during the instantiation of a class template.

Mike Miller:

I think the relevant specification is 14.6.4.1 [temp.point] paragraph 3, dealing with the point of instantiation:

For a class template specialization... if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

That means that the point of instantiation of A<B<int> > is before that of B<int>, not in the middle of B<int> after the declaration of B::X, and consequently a reference to B<int>::X from A<B<int> > is ill-formed.

To put it another way, I believe John's approach requires that there be an instantiation stack, with the results of partially-instantiated templates on the stack being available to instantiations above them. I don't think the Standard mandates that approach; as far as I can see, simply determining the implicit instantiations that need to be done, rewriting the definitions at their respective points of instantiation with parameters substituted (with appropriate "forward declarations" to allow for non-instantiating references), and compiling the result normally should be an acceptable implementation technique as well. That is, the implicit instantiation of the example (using, e.g., B_int to represent the generated name of the B<int> specialization) could be something like

        struct B_int;

        struct A_B_int {
            B_int::X x;    // error, incomplete type
        };

        struct B_int {
            typedef int* X;
            A_B_int a;
        };

Notes from 10/01 meeting:

This was discussed at length. The consensus was that the template case should be treated the same as the non-template class case it terms of the order in which members get declared/defined and classes get completed.

Proposed resolution:

In 14.6.4.1 [temp.point] paragraph 3 change:

the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

To:

the point of instantiation is the same as the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the nearest enclosing declaration. [Note: The point of instantiation is still at namespace scope but any declarations preceding the point of instantiation, even if not at namespace scope, are considered to have been seen.]

Add following paragraph 3:

If an implicitly instantiated class template specialization, class member specialization, or specialization of a class template references a class, class template specialization, class member specialization, or specialization of a class template containing a specialization reference that directly or indirectly caused the instantiation, the requirements of completeness and ordering of the class reference are applied in the context of the specialization reference.

and the following example

  template <class T> struct A {
          typename T::X x;
  };

  struct B {
          typedef int X;
          A<B> a;
  };

  template <class T> struct C {
          typedef T* X;
          A<C> a;
  };

  int main ()
  {
          C<int> c;
  }

Notes from the October 2002 meeting:

This needs work. Moved back to drafting status.

See also issues 595 and 1330.




1258. “Instantiation context” differs from dependent lookup rules

Section: 14.6.4.1  [temp.point]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-10

C++11 expanded the lookup rules for dependent function calls (14.6.4.2 [temp.dep.candidate] paragraph 1 bullet 2) to include functions with internal linkage; previously only functions with external linkage were considered. However, 14.6.4.1 [temp.point] paragraph 6 still says,

The instantiation context of an expression that depends on the template arguments is the set of declarations with external linkage declared prior to the point of instantiation of the template specialization in the same translation unit.

Presumably this wording was overlooked and should be harmonized with the new specification.




1845. Point of instantiation of a variable template specialization

Section: 14.6.4.1  [temp.point]     Status: drafting     Submitter: Richard Smith     Date: 2014-01-28

The current wording of 14.6.4.1 [temp.point] does not define the point of instantiation of a variable template specialization. Presumably replacing the references to “static data member of a class template” with “variable template” in paragraphs 1 and 8 would be sufficient.




1253. Generic non-template members

Section: 14.7  [temp.spec]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-06

Many statements in the Standard apply only to templates, for example, 14.6 [temp.res] paragraph 8:

If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.

This clearly should apply to non-template member functions of class templates, not just to templates per se. Terminology should be established to refer to these generic entities that are not actually templates.

Additional notes (August, 2012):

Among the generic entities that should be covered by such a term are default function arguments, as they can be instantiated independently. If issue 1330 is resolved as expected, exception-specifications should also be covered by the same term.

See also issue 1484.




1396. Deferred instantiation and checking of non-static data member initializers

Section: 14.7.1  [temp.inst]     Status: drafting     Submitter: Jason Merrill     Date: 2011-09-22

Non-static data member initializers get the same late parsing as member functions and default arguments, but are they also instantiated as needed like them? And when is their validity checked?

Notes from the October, 2012 meeting:

CWG agreed that non-static data member initializers should be handled like default arguments.

Additional note (March, 2013):

Determining whether a defaulted constructor is constexpr or not requires parsing the class's non-static data member initializers; see also issue 1360.




2072. Default argument instantiation for member functions of templates

Section: 14.7.1  [temp.inst]     Status: drafting     Submitter: Maxim Kartashev     Date: 2015-01-19

Default argument instantiation is described in 14.7.1 [temp.inst] paragraph 13, and although instantiation of default arguments for member functions of class templates is mentioned elsewhere a number of times, this paragraph only describes default argument instantiation for function templates.




1665. Declaration matching in explicit instantiations

Section: 14.7.2  [temp.explicit]     Status: drafting     Submitter: Richard Smith     Date: 2013-04-19

Consider a case like

  struct X {
    template<typename T> void f(T);
    void f(int);
  };
  template void X::f(int);

or

  template<typename T> void f(T) {}
  void f(int);
  template void f(int);

Presumably in both these cases the explicit instantiation should refer to the template and not to the non-template; however, 14.5.2 [temp.mem] paragraph 2 says,

A normal (non-template) member function with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied.

This would appear to give the wrong answer for the first example. It's not clearly stated, but consistency would suggest a similar wrong answer for the second. Presumably a statement is needed somewhere that an explicit instantiation directive applies to a template and not a non-template function if both are visible.

Additional note, January, 2014:

A related example has been raised:

  template<typename T> class Matrix {
  public:
    Matrix(){}
    Matrix(const Matrix&){}
    template<typename U>
      Matrix(const Matrix<U>&);
  };

  template Matrix<int>::Matrix(const Matrix&);

  Matrix<int> m;
  Matrix<int> mm(m);

If the explicit instantiation directive applies to the constructor template, there is no way to explicitly instantiate the copy constructor.




1704. Type checking in explicit instantiation of variable templates

Section: 14.7.2  [temp.explicit]     Status: drafting     Submitter: Richard Smith     Date: 2013-06-20

It is not clear whether the following is well-formed or not:

  template<typename T> int arr[sizeof(T)] = {};
  template int arr<int>[];

Are we supposed to instantiate the specialization and treat the explicit instantiation declaration as if it were a redeclaration (in which case the omitted array bound would presumably be OK), or is the type of the explicit instantiation declaration required to exactly match the type that the instantiated specialization has (in which case the omitted bound would presumably not be OK)? Or something else?

(See also issue 1728.)




1728. Type of an explicit instantiation of a variable template

Section: 14.7.2  [temp.explicit]     Status: drafting     Submitter: Larisse Voufo     Date: 2013-08-05

It is not clear to what extent the type in an explicit instantiation must match that of a variable template. For example:

  template<typename T> T var = T();
  template float var<float>;   // #1.
  template int* var<int>;      // #2.
  template auto var<char>;     // #3.

(See also issue 1704.)




529. Use of template<> with “explicitly-specialized” class templates

Section: 14.7.3  [temp.expl.spec]     Status: drafting     Submitter: James Widman     Date: 16 August 2005

Paragraph 17 of 14.7.3 [temp.expl.spec] says,

A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.

This is curious, because paragraph 3 only allows explicit specialization of members of implicitly-instantiated class specializations, not explicit specializations. Furthermore, paragraph 4 says,

Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the explicit specialization syntax.

Paragraph 18 provides a clue for resolving the apparent contradiction:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member.

It appears from this and the following example that the phrase “explicitly specialized” in paragraphs 17 and 18, when referring to enclosing class templates, does not mean that explicit specializations have been declared for them but that their names in the qualified-id are followed by template argument lists. This terminology is confusing and should be changed.

Proposed resolution (October, 2005):

  1. Change 14.7.3 [temp.expl.spec] paragraph 17 as indicated:

  2. A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized specialization. [Example:...
  3. Change 14.7.3 [temp.expl.spec] paragraph 18 as indicated:

  4. In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well that is, the template-id naming the template may be composed of template parameter names rather than template-arguments. In For each unspecialized template in such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. In such declarations, an unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member. [Example:...

Notes from the April, 2006 meeting:

The revised wording describing “unspecialized” templates needs more work to ensure that the parameter names in the template-id are in the correct order; the distinction between template arguments and parameters is also probably not clear enough. It might be better to replace this paragraph completely and avoid the “unspecialized” wording altogether.

Proposed resolution (February, 2010):

  1. Change 14.7.3 [temp.expl.spec] paragraph 17 as follows:

  2. A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized specialization. [Example:...
  3. Change 14.7.3 [temp.expl.spec] paragraph 18 as follows:

  4. In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. that is, the corresponding template prefix may specify a template-parameter-list instead of template<> and the template-id naming the template be written using those template-parameters as template-arguments. In such a declaration, the number, kinds, and types of the template-parameters shall be the same as those specified in the primary template definition, and the template-parameters shall be named in the template-id in the same order that they appear in the template-parameter-list. An unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member. [Example:...



727. In-class explicit specializations

Section: 14.7.3  [temp.expl.spec]     Status: drafting     Submitter: Faisal Vali     Date: 5 October, 2008

14.7.3 [temp.expl.spec] paragraph 2 requires that explicit specializations of member templates be declared in namespace scope, not in the class definition. This restriction does not apply to partial specializations of member templates; that is,

    struct A {
      template<class T> struct B;
      template <class T> struct B<T*> { }; // well-formed
      template <> struct B<int*> { }; // ill-formed
    };

There does not seem to be a good reason for this inconsistency.

Additional note (October, 2013):

EWG has requested CWG to consider resolving this issue. See EWG issue 41.

Additional note, November, 2014:

See also paper N4090.




1840. Non-deleted explicit specialization of deleted function template

Section: 14.7.3  [temp.expl.spec]     Status: drafting     Submitter: Richard Smith     Date: 2014-01-19

The resolution of issue 941 permits a non-deleted explicit specialization of a deleted function template. For example:

  template<typename T> void f() = delete;
  decltype(f<int>()) *p;
  template<> void f<int>();

However, the existing normative wording is not adequate to handle this usage. For one thing, =delete is formally, at least, a function definition, and an implementation is not permitted to instantiate a function definition unless it is used; presumably, then, an implementation could not reject the decltype above as a reference to a deleted specialization. Furthermore, there should be a requirement that a non-deleted explicit specialization of a deleted function template must precede any reference to that specialization. (I.e., the example should be ill-formed as written but well-formed if the last two lines were interchanged.)




1993. Use of template<> defining member of explicit specialization

Section: 14.7.3  [temp.expl.spec]     Status: drafting     Submitter: Richard Smith     Date: 2014-08-31

Issue 531 surveyed existing practice at the time and determined that the most common syntax for defining a member of an explicit specialization used the template<> prefix. This approach, however, does not seem consistent, since such a definition is not itself an explicit specialization.




2055. Explicitly-specified non-deduced parameter packs

Section: 14.8.1  [temp.arg.explicit]     Status: drafting     Submitter: Jonathan Caves     Date: 2014-12-09

According to 14.8.1 [temp.arg.explicit] paragraph 3,

Trailing template arguments that can be deduced (14.8.2 [temp.deduct]) or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack (14.5.3 [temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments. If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted. In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.

It is not clear that this permits an example like:

  template<typename... T> void f(typename T::type...)   {
  }

  int main() {
    f<>();
  }

See also issue 2105.




1172. “instantiation-dependent” constructs

Section: 14.8.2  [temp.deduct]     Status: drafting     Submitter: Adamczyk     Date: 2010-08-05

There are certain constructs that are not covered by the existing categories of “type dependent” and “value dependent.” For example, the expression sizeof(sizeof(T())) is neither type-dependent nor value-dependent, but its validity depends on whether T can be value-constructed. We should be able to overload on such characteristics and select via deduction failure, but we need a term like “instantiation-dependent” to describe these cases in the Standard. The phrase “expression involving a template parameter” seems to come pretty close to capturing this idea.

Notes from the November, 2010 meeting:

The CWG favored extending the concepts of “type-dependent” and “value-dependent” to cover these additional cases, rather than adding a new concept.

Notes from the March, 2011 meeting:

The CWG reconsidered the direction from the November, 2010 meeting, as it would make more constructs dependent, thus requiring more template and typename keywords, resulting in worse error messages, etc.

Notes from the August, 2011 meeting:

The following example (from issue 1273) was deemed relevant for this issue:

    template <class T> struct C;

    class A {
       int i;
       friend struct C<int>;
    } a;

    class B {
       int i;
       friend struct C<float>;
    } b;

    template <class T>
    struct C {
       template <class U> decltype (a.i) f() { } // #1
       template <class U> decltype (b.i) f() { } // #2
    };

    int main() {
       C<int>().f<int>();     // calls #1
       C<float>().f<float>(); // calls #2
    }



1322. Function parameter type decay in templates

Section: 14.8.2  [temp.deduct]     Status: drafting     Submitter: Jason Merrill     Date: 2011-05-19

The discussion of issue 1001 seemed to have settled on the approach of doing the 8.3.5 [dcl.fct] transformations immediately to the function template declaration, so that the original form need not be remembered. However, the example in 14.8.2 [temp.deduct] paragraph 8 suggests otherwise:

  template <class T> int f(T[5]);
  int I = f<int>(0);
  int j = f<void>(0); // invalid array

One way that might be addressed would be to separate the concepts of the type of the template that participates in overload resolution and function matching from the type of the template that is the source for template argument substitution. (See also the example in paragraph 3 of the same section.)

Notes, January, 2012:




1582. Template default arguments and deduction failure

Section: 14.8.2  [temp.deduct]     Status: drafting     Submitter: John Spicer     Date: 2012-10-31

According to 14.8.2 [temp.deduct] paragraph 5,

The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. If a template argument has not been deduced and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default argument. If the substitution results in an invalid type, as described above, type deduction fails.

This leaves the impression that default arguments are used after deduction failure leaves an argument undeduced. For example,

  template<typename T> struct Wrapper;
  template<typename T = int> void f(Wrapper<T>*);
  void g() {
    f(0);
  }

Deduction fails for T, so presumably int is used. However, some implementations reject this code. It appears that the intent would be better expressed as something like

...If a template argument is used only in a non-deduced context and its corresponding template parameter has a default argument...

Rationale (November, 2013):

CWG felt that this issue should be considered by EWG in a broader context before being resolved.

Additional note, April, 2015:

EWG has requested that CWG resolve this issue along the lines discussed above.

Notes from the May, 2015 meeting:

CWG agreed that a default template argument should only be used if the parameter is not used in a deducible context. See also issue 2092.




1724. Unclear rules for deduction failure

Section: 14.8.2  [temp.deduct]     Status: drafting     Submitter: James Widman     Date: 2013-07-31

According to 14.8.2 [temp.deduct] paragraph 8,

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, with a diagnostic required, if written using the substituted arguments.

Presumably the phrase “if written” refers to rewriting the template declaration in situ with the substituted arguments, rather than writing that type or expression at some arbitrary location, e.g.,

  void g(double) = delete;

  template<class T> auto f(T t) -> decltype(g(t));

  void g(int);

  void h() {
    typedef int T;
    T t = 42;
    g(t);  // Ok (I “wrote the substituted arguments”, and it seems fine)
    f(42); // Presumably substitution is meant to fail.
  }

Perhaps a clearer formulation could be used?




1844. Defining “immediate context”

Section: 14.8.2  [temp.deduct]     Status: drafting     Submitter: Richard Smith     Date: 2014-01-28

The handling of an example like

  template<typename T, std::size_t S = sizeof(T)> struct X {};
  template<typename T> X<T> foo(T*);
  void foo(...);

  void test() { struct S *s; foo(s); }

varies among implementations, presumably because the meaning of “immediate context” in determining whether an error is a substitution failure or a hard error is not clearly defined.

Notes from the February, 2016 meeting:

See also issue 1554; the resolution of this issue should also deal with alias templates.




1513. initializer_list deduction failure

Section: 14.8.2.1  [temp.deduct.call]     Status: drafting     Submitter: Steve Adamczyk     Date: 2012-06-28

According to 14.8.2.1 [temp.deduct.call] paragraph 1,

If removing references and cv-qualifiers from P gives std::initializer_list<P'> for some P' and the argument is an initializer list (8.6.4 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5 [temp.deduct.type]).

It is not entirely clear whether the deduction for an initializer list meeting a std::initializer_list<T> is a recursive subcase, or part of the primary deduction. A relevant question is: if the deduction on that part fails, does the entire deduction fail, or is the parameter to be considered non-deduced?

Notes from the October, 2012 meeting:

CWG determined that the entire deduction fails in this case.




1584. Deducing function types from cv-qualified types

Section: 14.8.2.1  [temp.deduct.call]     Status: drafting     Submitter: Daniel Krügler     Date: 2012-11-04

It is not clear whether the following is well-formed or not:

  void foo(){}

  template<class T>
  void deduce(const T*) { }

  int main() {
    deduce(foo);
  }

Implementations vary in their treatment of this example.

Proposed resolution (April, 2013):

Change 14.8.2.5 [temp.deduct.type] paragraph 18 as follows:

A template-argument can be deduced from a function, pointer to function, or pointer to member function type. [Note: cv-qualification of a deduced function type is ignored; see 8.3.5 [dcl.fct]. —end note] [Example:

  template<class T> void f(void(*)(T,int));
  template<class T> void f2(const T*);
  template<class T> void foo(T,int);
  void g(int,int);
  void g(char,int);
  void g2();

  void h(int,int,int);
  void h(char,int);
  int m() {
    f(&g);     // error: ambiguous
    f(&h);     // OK: void h(char,int) is a unique match
    f(&foo);   // error: type deduction fails because foo is a template
    f2(g2);    // OK: cv-qualification of deduced function type ignored
  }

end example]

Additional note, November, 2014:

Concern was expressed regarding the proposed resolution over its treatment of an example like the following:

  template<typename T> struct tuple_size {};
  template<typename T> struct tuple_size<T const>: tuple_size<T> {};

  tuple_size<void()> t;

In this case T const is always considered to be more specialized for void(), leading to infinite self-derivation.

The issue has been returned to "open" status for further consideration.

Notes from the May, 2015 meeting:

The consensus of CWG was that the cv-qualification of the argument and parameter must match, so the original example should be rejected.




1939. Argument conversions to nondeduced parameter types revisited

Section: 14.8.2.1  [temp.deduct.call]     Status: drafting     Submitter: Richard Smith     Date: 2014-06-11

The intent of the resolution of issue 1184 appears not to have been completely realized. In particular, the phrase, “contains no template-parameters that participate in template argument deduction” in both the note in 14.8.2.1 [temp.deduct.call] paragraph 4 and the normative wording in 14.8.1 [temp.arg.explicit] paragraph 6 is potentially misleading and probably should say something like, “contains no template-parameters outside non-deduced contexts.” Also, the normative wording should be moved to 14.8.2.1 [temp.deduct.call] paragraph 4, since it applies when there are no explicitly-specified template arguments. For example,

  template<typename T>
  void f(T, typename identity<T>::type*);

Presumably the second parameter should allow pointer conversions, even though it does contain a template-parameter that participates in deduction (via the first function parameter).

Additional note, October, 2015:

See also issue 1391.




1486. Base-derived conversion in member pointer deduction

Section: 14.8.2.2  [temp.deduct.funcaddr]     Status: drafting     Submitter: John Spicer     Date: 2012-03-26

The rules for deducing template arguments when taking the address of a function template in 14.8.2.2 [temp.deduct.funcaddr] do not appear to allow for a base-to-derived conversion in a case like:

  struct Base {
    template<class U> void f(U);
  };

  struct Derived : Base { };

  int main() {
    void (Derived::*pmf)(int) = &Derived::f;
  } 

Most implementations appear to allow this adjustment, however.




1610. Cv-qualification in deduction of reference to array

Section: 14.8.2.4  [temp.deduct.partial]     Status: drafting     Submitter: Richard Smith     Date: 2013-01-28

Given

   template<class C> void foo(const C* val) {}
   template<int N> void foo(const char (&t)[N]) {}

it is intuitive that the second template is more specialized than the first. However, the current rules make them unordered. In 14.8.2.4 [temp.deduct.partial] paragraph 4, we have P as const C* and A as const char (&)[N]. Paragraph 5 transforms A to const char[N]. Finally, paragraph 7 removes top-level cv-qualification; since a cv-qualified array element type is considered to be cv-qualification of the array (_4567_.3.9.3 [basic.type.qualifier] paragraph 5, cf issue 1059), A becomes char[N]. P remains const C*, so deduction fails because of the missing const in A.

Notes from the April, 2013 meeting:

CWG agreed that the const should be preserved in the array type.




2088. Late tiebreakers in partial ordering

Section: 14.8.2.4  [temp.deduct.partial]     Status: drafting     Submitter: Richard Smith     Date: 2015-02-19

The late tiebreakers for lvalue-vs-rvalue references and cv-qualification in 14.8.2.4 [temp.deduct.partial] paragraph 9 are applied

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

However, this is based on a false assumption. For example,

  template <typename T> struct A {
    struct typeA { };
    struct typeB { };
    using convTyA = T (*const &&)(typename A<T>::typeA);
    using convTyB = T (*const &)(typename A<T>::typeB);
    operator convTyA();
    operator convTyB();
  };

  template <typename T> void foo(T (*const &&)(typename A<T>::typeA));
  template <typename T> int foo(T (*const &)(typename A<T>::typeB));

  int main() {
    return foo<int>(A<int>());
  }

(see also issues 1847 and 1157.). We need to decide whether the rule is “deduction succeeds in both directions” or “the types are identical.” The latter seems more reasonable.




2092. Deduction failure and overload resolution

Section: 14.8.3  [temp.over]     Status: drafting     Submitter: Fedor Sergeev     Date: 2015-03-06

Given an example like

  template <class T = int> void foo(T*);

  void test()
  {
    foo(0);   // #1 valid?
    foo<>(0);  // #2 valid?
  }

most/all implementations reject this code. However, the wording of the Standard only invokes 14.8.3 [temp.over] (“Overload resolution”) in cases where there is more than one function or function template, which is not the case here. The current wording would appear to make this well-formed because of the application of 14.8.1 [temp.arg.explicit] paragraph 2. Perhaps overload resolution should apply even when there is a single function template?

Notes from the May, 2015 meeting:

This issue is mostly a duplicate of issue 1582. However, CWG felt that it should be clarified that overload resolution applies in all cases, not just when templates are overloaded, so the issue is being left open to deal with that aspect.




2172. Multiple exceptions with one exception object

Section: 15.3  [except.handle]     Status: drafting     Submitter: Richard Smith     Date: 2015-09-14

During the discussion of issue 2098 it was observed that multiple exceptions may share a single exception object via std::exception_ptr. It is not clear that the current wording handles that case correctly.




1436. Interaction of constant expression changes with preprocessor expressions

Section: 16.1  [cpp.cond]     Status: drafting     Submitter: Richard Smith     Date: 2012-01-02

It appears that some of the recent changes to the description of constant expressions have allowed constructs into preprocessor expressions that do not belong there. Some changes are required to restrict the current capabilities of constant expressions to what is intended to be allowed in preprocessor expressions.

Proposed resolution (February, 2012):

  1. Change 16.1 [cpp.cond] paragraph 2 as follows:

  2. Each preprocessing token that remains (in the list of preprocessing tokens that will become the controlling expression) after all macro replacements have occurred shall be in the lexical form of a token (2.6 [lex.token]). Any such token that is a literal (2.13.1 [lex.literal.kinds]) shall be an integer-literal, a character-literal, or a boolean-literal.
  3. Change 16.1 [cpp.cond] paragraph 4 as follows:

  4. ...using arithmetic that has at least the ranges specified in 18.3 [support.limits]. The only operators permitted in the controlling constant expression are ?:, ||, &&, |, ^, &, ==, !=, <, <=, >, >=, <<, >>, -, +, *, /, %, !, and ~. For the purposes of this token conversion...



1718. Macro invocation spanning end-of-file

Section: 16.3  [cpp.replace]     Status: drafting     Submitter: David Krauss     Date: 2013-07-23

Although it seems to be common implementation practice to reject a macro invocation that begins in a header file and whose closing right parenthesis appears in the file that included it, there does not seem to be a prohibition of this case in the specification of function-style macros. Should this be accepted?

Notes from the February, 2014 meeting:

CWG agreed that macro invocations spanning file boundaries should be prohibited. Resolution of this issue should be coordinated with WG14.




2003. Zero-argument macros incorrectly specified

Section: 16.3  [cpp.replace]     Status: drafting     Submitter: Richard Smith     Date: 2014-09-12

According to 16.3 [cpp.replace] paragraph 4,

If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition.

That is, a sequence of no preprocessing tokens counts as an argument. That phrasing has problems with zero-argument function-like macros, e.g.,

  #define M()
  M();

M is defined as having no parameters but the invocation has one (empty) argument, which does not match the number of parameters in the definition.




1335. Stringizing, extended characters, and universal-character-names

Section: 16.3.2  [cpp.stringize]     Status: drafting     Submitter: Johannes Schaub     Date: 2011-07-03

When a string literal containing an extended character is stringized (16.3.2 [cpp.stringize]), the result contains a universal-character-name instead of the original extended character. The reason is that the extended character is translated to a universal-character-name in translation phase 1 (2.2 [lex.phases]), so that the string literal "@" (where @ represents an extended character) becomes "\uXXXX". Because the preprocessing token is a string literal, when the stringizing occurs in translation phase 4, the \ is doubled, and the resulting string literal is "\"\\uXXXX\"". As a result, the universal-character-name is not recognized as such when the translation to the execution character set occurs in translation phase 5. (Note that phase 5 translation does occur if the stringized extended character does not appear in a string literal.) Existing practice appears to ignore these rules and preserve extended characters in stringized string literals, however.

See also issue 578.

Additional note (August, 2013):

Implementations are granted substantial latitude in their handling of extended characters and universal-character-names in 2.2 [lex.phases] paragraph 1 phase 1, i.e.,

(An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.)

However, this freedom is mostly nullified by the requirements of stringizing in 16.3.2 [cpp.stringize] paragraph 2:

If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character string literal preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument.

This means that, in order to handle a construct like

  #define STRINGIZE_LITERAL( X ) # X
  #define STRINGIZE( X ) STRINGIZE_LITERAL( X )

  STRINGIZE( STRINGIZE( identifier_\u00fC\U000000Fc ) ) 

an implementation must recall the original spelling, including the form of UCN and the capitalization of any non-numeric hexadecimal digits, rather than simply translating the characters into a convenient internal representation.

To effect the freedom asserted in 2.2 [lex.phases], the description of stringizing should make the spelling of a universal-character-name implementation-defined.




1709. Stringizing raw string literals containing newline

Section: 16.3.2  [cpp.stringize]     Status: drafting     Submitter: David Krauss     Date: 2013-07-01

Stringizing a raw string literal containing a newline produces an invalid (unterminated) string literal and hence results in undefined behavior. It should be specified that a newline in a string literal is transformed to the two characters '\' 'n' in the resulting string literal.

A slightly related case involves stringizing a bare backslash character: because backslashes are only escaped within a string or character literal, a stringized bare backslash becomes "\", which is invalid and hence results in undefined behavior.




2181. Normative requirements in an informative Annex

Section: B  [implimits]     Status: drafting     Submitter: Sean Hunt     Date: 2015-10-18

According to B [implimits] paragraph 1,

Because computers are finite, C++ implementations are inevitably limited in the size of the programs they can successfully process. Every implementation shall document those limitations where known.

Because Annex B [implimits] is informative, not normative, it should not use “shall.”




1279. Additional differences between C++ 2003 and C++ 2011

Section: C.2  [diff.cpp03]     Status: drafting     Submitter: Nikolay Ivchenkov     Date: 2011-03-27

A number of differences between C++03 and C++11 were omitted from C.2 [diff.cpp03]:

Additional note (January, 2012):

In addition to the items previously mentioned, access declarations were removed from C++11 but are not mentioned in C.2 [diff.cpp03].

Proposed (partial) resolution (February, 2012):

Add the following as a new section in C.2 [diff.cpp03]:

C.2.5 Clause 11 [class.access]: member access control pdiff.cpp03.class.access

Change: Remove access declarations.

Rationale: Removal of feature deprecated since C++ 1998.

Effect on original feature: Valid C++ 2003 code that uses access declarations is ill-formed in this International Standard. Instead, using-declarations (7.3.3 [namespace.udecl]) can be used.






Issues with "Open" Status


916. Does a reference type have a destructor?

Section: _N2914_.14.10.2.1  [concept.map.fct]     Status: open     Submitter: James Widman     Date: 12 June, 2009

Is the following well-formed?

    auto concept HasDestructor<typename T> {
      T::~T();
    }

    concept_map HasDestructor<int&> { }

According to _N2914_.14.10.2.1 [concept.map.fct] paragraph 4, the destructor requirement in the concept map results in an expression x.~X(), where X is the type int&. According to 5.2.4 [expr.pseudo], this expression is ill-formed because the object type and the type-name must be the same type, but the object type cannot be a reference type (references are dropped from types used in expressions, 5 [expr] paragraph 5).

It is not clear whether this should be addressed by changing 5.2.4 [expr.pseudo] or _N2914_.14.10.2.1 [concept.map.fct].




783. Definition of “argument”

Section: 1.3  [intro.defs]     Status: open     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 3

The definition of an argument does not seem to cover many assumed use cases, and we believe that is not intentional. There should be answers to questions such as: Are lambda-captures arguments? Are type names in a throw-spec arguments? “Argument” to casts, typeid, alignof, alignas, decltype and sizeof? why in x[arg] arg is not an argument, but the value forwarded to operator[]() is? Does not apply to operators as call-points not bounded by parentheses? Similar for copy initialization and conversion? What are deduced template “arguments?” what are “default arguments?” can attributes have arguments? What about concepts, requires clauses and concept_map instantiations? What about user-defined literals where parens are not used?




949. Requirements for freestanding implementations

Section: 1.4  [intro.compliance]     Status: open     Submitter: Detlef Vollman     Date: 2 August, 2009

According to 1.4 [intro.compliance] paragraph 7,

A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries (17.6.1.3 [compliance]).

This definition links two relatively separate topics: the lack of an operating system and the minimal set of libraries. Furthermore, 3.6.1 [basic.start.main] paragraph 1 says:

[Note: in a freestanding environment, start-up and termination is implementation-defined; start-up contains the execution of constructors for objects of namespace scope with static storage duration; termination contains the execution of destructors for objects with static storage duration. —end note]

It would be helpful if the two characteristics (lack of an operating system and restricted set of libraries) were named separately and if these statements were clarified to identify exactly what is implementation-defined.

Notes from the October, 2009 meeting:

The CWG felt that it needed a specific proposal in a paper before attempting to resolve this issue.




1953. Data races and common initial sequence

Section: 1.7  [intro.memory]     Status: open     Submitter: Faisal Vali     Date: 2014-06-23

According to 1.7 [intro.memory] paragraph 3,

A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having non-zero width. [Note: Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation. —end note] Two or more threads of execution (1.10 [intro.multithread]) can update and access separate memory locations without interfering with each other.

It is not clear how this relates to the permission granted in 9.2 [class.mem] paragraph 18 to inspect the common initial sequence of standard-layout structs that are members of a standard-layout union. If one thread is writing to the common initial sequence and another is reading from it via a different struct, that should constitute a data race, but the current wording does not clearly state that.




698. The definition of “sequenced before” is too narrow

Section: 1.9  [intro.execution]     Status: open     Submitter: Jens Maurer     Date: 13 July, 2008

According to 1.9 [intro.execution] paragraph 14, “sequenced before” is a relation between “evaluations.” However, 3.6.3 [basic.start.dynamic] paragraph 3 says,

If the completion of the initialization of a non-local object with static storage duration is sequenced before a call to std::atexit (see <cstdlib>, 18.5 [support.start.term]), the call to the function passed to std::atexit is sequenced before the call to the destructor for the object. If a call to std::atexit is sequenced before the completion of the initialization of a non-local object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std::atexit. If a call to std::atexit is sequenced before another call to std::atexit, the call to the function passed to the second std::atexit call is sequenced before the call to the function passed to the first std::atexit call.

Except for the calls to std::atexit, these events do not correspond to “evaluation” of expressions that appear in the program. If the “sequenced before” relation is to be applied to them, a more comprehensive definition is needed.




578. Phase 1 replacement of characters with universal-character-names

Section: 2.2  [lex.phases]     Status: open     Submitter: Martin Vejnár     Date: 7 May 2006

According to 2.2 [lex.phases] paragraph 1, in translation phase 1,

Any source file character not in the basic source character set (2.3 [lex.charset]) is replaced by the universal-character-name that designates that character.

If a character that is not in the basic character set is preceded by a backslash character, for example

    "\á"

the result is equivalent to

    "\\u00e1"

that is, a backslash character followed by the spelling of the universal-character-name. This is different from the result in C99, which accepts characters from the extended source character set without replacing them with universal-character-names.

See also issue 1335.




1698. Files ending in \

Section: 2.2  [lex.phases]     Status: open     Submitter: David Krauss     Date: 2013-06-10

The description of how to handle file not ending in a newline in 2.2 [lex.phases] paragraph 1, phase 2, is:

  1. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. If, as a result, a character sequence that matches the syntax of a universal-character-name is produced, the behavior is undefined. A source file that is not empty and that does not end in a new-line character, or that ends in a new-line character immediately preceded by a backslash character before any such splicing takes place, shall be processed as if an additional new-line character were appended to the file.

This is not clear regarding what happens if the last character in the file is a backslash. In such a case, presumably the result of adding the newline should not be a line splice but rather a backslash preprocessing-token (that will be diagnosed as an invalid token in phase 7), but that should be spelled out.




1403. Universal-character-names in comments

Section: 2.7  [lex.comment]     Status: open     Submitter: David Krauss     Date: 2011-10-05

According to 2.3 [lex.charset] paragraph 2,

If the hexadecimal value for a universal-character-name corresponds to a surrogate code point (in the range 0xD800-0xDFFF, inclusive), the program is ill-formed. Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed.

These restrictions should not apply to comment text. Arguably the prohibitions of control characters and characters in the basic character set already do not apply, as they require that the preprocessing tokens for literals have already been recognized; this occurs in phase 3, which also replaces comments with single spaces. However, the prohibition of surrogate code points is not so limited and might conceivably be applied within comments.

Probably the most straightforward way of addressing this problem would be simply to state in 2.7 [lex.comment] that character sequences that resemble universal-character-names are not recognized as such within comment text.




1972. Identifier character restrictions in non-identifiers

Section: 2.10  [lex.name]     Status: open     Submitter: Richard Smith     Date: 2014-07-15

According to 2.10 [lex.name] paragraph 1,

Each universal-character-name in an identifier shall designate a character whose encoding in ISO 10646 falls into one of the ranges specified in E.1 [charname.allowed].

However, identifier-nondigit is also used in the grammar for pp-number. Should this restriction also be understood to apply in that non-identifier context?




411. Use of universal-character-name in character versus string literals

Section: 2.13.5  [lex.string]     Status: open     Submitter: James Kanze     Date: 23 Apr 2003

2.13.5 [lex.string] paragraph 5 reads

Escape sequences and universal-character-names in string literals have the same meaning as in character literals, except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \. In a narrow string literal, a universal-character-name may map to more than one char element due to multibyte encoding.

The first sentence refers us to 2.13.3 [lex.ccon], where we read in the first paragraph that "An ordinary character literal that contains a single c-char has type char [...]." Since the grammar shows that a universal-character-name is a c-char, something like '\u1234' must have type char (and thus be a single char element); in paragraph 5, we read that "A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implemenation-defined encoding."

This is in obvious contradiction with the second sentence. In addition, I'm not really clear what is supposed to happen in the case where the execution (narrow-)character set is UTF-8. Consider the character \u0153 (the oe in the French word oeuvre). Should '\u0153' be a char, with an "error" value, say '?' (in conformance with the requirement that it be a single char), or an int, with the two char values 0xC5, 0x93, in an implementation defined order (in conformance with the requirement that a character representable in the execution character set be represented). Supposing the former, should "\u0153" be the equivalent of "?" (in conformance with the first sentence), or "\xC5\x93" (in conformance with the second).

Notes from October 2003 meeting:

We decided we should forward this to the C committee and let them resolve it. Sent via e-mail to John Benito on November 14, 2003.

Reply from John Benito:

I talked this over with the C project editor, we believe this was handled by the C committee before publication of the current standard.

WG14 decided there needed to be a more restrictive rule for one-to-one mappings: rather than saying "a single c-char" as C++ does, the C standard says "a single character that maps to a single-byte execution character"; WG14 fully expect some (if not many or even most) UCNs to map to multiple characters.

Because of the fundamental differences between C and C++ character types, I am not sure the C committee is qualified to answer this satisfactorily for WG21. WG14 is willing to review any decision reached for compatibility.

I hope this helps.

(See also issue 912 for a related question.)




1266. user-defined-integer-literal overflow

Section: 2.13.8  [lex.ext]     Status: open     Submitter: Michael Wong     Date: 2011-03-20

The decimal-literal in a user-defined-integer-literal might be too large for an unsigned long long to represent (in implementations with extended integer types). In such cases, the original intent appears to have been to call a raw literal operator or a literal operator template; however, the existing wording of 2.13.8 [lex.ext] paragraph 3 always calls the unsigned long long literal operator if it exists, regardless of the value of the decimal-literal.




1209. Is a potentially-evaluated expression in a template definition a “use?”

Section: 3.2  [basic.def.odr]     Status: open     Submitter: Johannes Schaub     Date: 2010-10-08

Consider the following complete program:

    void f();
    template<typename T> void g() { f(); }
    int main() { }

Must f() be defined to make this program well-formed? The current wording of 3.2 [basic.def.odr] does not make any special provision for expressions that appear only in uninstantiated template definitions.

(See also issue 1254.)


2240. this is not odr-used in a constant expression

Section: 3.2  [basic.def.odr]     Status: open     Submitter: Daveed Vandevoorde     Date: 2016-03-04

[Detailed description pending.]




2009. Unclear specification of class scope

Section: 3.3.7  [basic.scope.class]     Status: open     Submitter: Richard Smith     Date: 2014-09-23

Point 2 of the rules of class scope in 3.3.7 [basic.scope.class] paragraph 1 says,

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

It is not clear that this provision does not apply to names appearing in function bodies, default arguments, exception-specifications, and brace-or-equal-initializers. It is also not clear what it means to “re-evaluate” a name.

One possible approach to this problem would be to say that all names declared in a class are visible throughout the class and simply make it ill-formed to refer to a name that has not been declared yet in the contexts in which that is problematic, such as types and template arguments.

In addition, the fourth point says,

A name declared within a member function hides a declaration of the same name whose scope extends to or past the end of the member function's class.

This rule is unneeded, as it simply restates the normal hiding rule in 3.3.1 [basic.scope.declarative] paragraph 1:

The scope of a declaration is the same as its potential scope unless the potential scope contains another declaration of the same name. In that case, the potential scope of the declaration in the inner (contained) declarative region is excluded from the scope of the declaration



191. Name lookup does not handle complex nesting

Section: 3.4.1  [basic.lookup.unqual]     Status: open     Submitter: Alan Nash     Date: 29 Dec 1999

The current description of unqualified name lookup in 3.4.1 [basic.lookup.unqual] paragraph 8 does not correctly handle complex cases of nesting. The Standard currently reads,

A name used in the definition of a function that is a member function (9.3) of a class X shall be declared in one of the following ways:
In particular, this formulation does not handle the following example:
    struct outer {
        static int i;
        struct inner {
            void f() {
                struct local {
                    void g() {
                        i = 5;
                    }
                };
            }
        };
    };
Here the reference to i is from a member function of a local class of a member function of a nested class. Nothing in the rules allows outer::i to be found, although intuitively it should be found.

A more comprehensive formulation is needed that allows traversal of any combination of blocks, local classes, and nested classes. Similarly, the final bullet needs to be augmented so that a function need not be a (direct) member of a namespace to allow searching that namespace when the reference is from a member function of a class local to that function. That is, the current rules do not allow the following example:

    int j;    // global namespace
    struct S {
        void f() {
            struct local2 {
                void g() {
                    j = 5;
                }
            };
        }
    };



405. Unqualified function name lookup

Section: 3.4.1  [basic.lookup.unqual]     Status: open     Submitter: William M. Miller     Date: 14 Apr 2003

There seems to be some confusion in the Standard regarding the relationship between 3.4.1 [basic.lookup.unqual] (Unqualified name lookup) and 3.4.2 [basic.lookup.argdep] (Argument-dependent lookup). For example, 3.4.1 [basic.lookup.unqual] paragraph 3 says,

The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 [basic.lookup.argdep].

In other words, nothing in 3.4.1 [basic.lookup.unqual] applies to function names; the entire lookup is described in 3.4.2 [basic.lookup.argdep].

3.4.2 [basic.lookup.argdep] does not appear to share this view of its responsibility. The closest it comes is in 3.4.2 [basic.lookup.argdep] paragraph 2a:

...the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.

Presumably, "ordinary unqualified lookup" is a reference to the processing described in 3.4.1 [basic.lookup.unqual], but, as noted above, 3.4.1 [basic.lookup.unqual] explicitly precludes applying that processing to function names. The details of "ordinary unqualified lookup" of function names are not described anywhere.

The other clauses that reference 3.4.2 [basic.lookup.argdep], clauses 13 [over] and 14 [temp], are split over the question of the relationship between 3.4.1 [basic.lookup.unqual] and 3.4.2 [basic.lookup.argdep]. 13.3.1.1.1 [over.call.func] paragraph 3, for instance, says

The name is looked up in the context of the function call following the normal rules for name lookup in function calls (3.4.2 [basic.lookup.argdep]).

I.e., this reference assumes that 3.4.2 [basic.lookup.argdep] is self-contained. The same is true of 13.3.1.2 [over.match.oper] paragraph 3, second bullet:

The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2 [basic.lookup.argdep]), except that all member functions are ignored.

On the other hand, however, 14.6.4.2 [temp.dep.candidate] paragraph 1 explicitly assumes that 3.4.1 [basic.lookup.unqual] and 3.4.2 [basic.lookup.argdep] are both involved in function name lookup and do different things:

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

Suggested resolution:

Change 3.4.1 [basic.lookup.unqual] paragraph 1 from

...name lookup ends as soon as a declaration is found for the name.

to

...name lookup ends with the first scope containing one or more declarations of the name.

Change the first sentence of 3.4.1 [basic.lookup.unqual] paragraph 3 from

The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 [basic.lookup.argdep].

to

An unqualified name used as the postfix-expression of a function call is looked up as described below. In addition, argument-dependent lookup (3.4.2 [basic.lookup.argdep]) is performed on this name to complete the resulting set of declarations.



1200. Lookup rules for template parameters

Section: 3.4.1  [basic.lookup.unqual]     Status: open     Submitter: Johannes Schaub     Date: 2010-09-18

Although 3.3.9 [basic.scope.temp] now describes the scope of a template parameter, the description of unqualified name lookup in 3.4.1 [basic.lookup.unqual] do not cover uses of template parameter names. The note in 3.4.1 [basic.lookup.unqual] paragraph 16 says,

the rules for name lookup in template definitions are described in 14.6 [temp.res].

but the rules there cover dependent and non-dependent names, not template parameters themselves.




1771. Restricted lookup in nested-name-specifier

Section: 3.4.3  [basic.lookup.qual]     Status: open     Submitter: Canada     Date: 2013-09-24

N3690 comment CA 21

Consider the following example:

  template <typename T> struct B { }; 
  namespace N { 
    namespace L { 
      template <int> void A(); 
    } 
    namespace M { 
      template <int> struct A { typedef int y; }; 
    } 
    using namespace L; 
    using namespace M; 
  } 
  B<N::/*template */A<0>::y> (x);

Which A is referenced in the last line? According to 3.4.3 [basic.lookup.qual] paragraph 1,

If a :: scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that :: considers only namespaces, types, and templates whose specializations are types.

It is not clear whether this applies to the example or not, and the interpretation of the < token depends on the result of the lookup.

Notes from the September, 2013 meeting:

The restricted lookup mentioned in 3.4.3 [basic.lookup.qual] paragraph 1 is based on a one-token lookahead; because the next token following A in the example is not ::, the restricted lookup does not apply, and the result is ambiguous. Uncommenting the template keyword in the example does not affect the lookup.




562. qualified-ids in non-expression contexts

Section: 3.4.3.1  [class.qual]     Status: open     Submitter: Mike Miller     Date: 6 April 2006

Both 3.4.3.1 [class.qual] and 3.4.3.2 [namespace.qual] specify that some lookups are to be performed “in the context of the entire postfix-expression,” ignoring the fact that qualified-ids can appear outside of expressions.

It was suggested in document J16/05-0156 = WG21 N1896 that these uses be changed to “the context in which the qualified-id occurs,” but it isn't clear that this formulation adequately covers all the places a qualified-id can occur.




278. External linkage and nameless entities

Section: 3.5  [basic.link]     Status: open     Submitter: Daveed Vandevoorde     Date: 12 Apr 2000

It is unclear to what extent entities without names match across translation units. For example,

    struct S {
       int :2;
       enum { a, b, c } x;
       static class {} *p;
    };

If this declaration appears in multiple translation units, are all these members "the same" in each declaration?

A similar question can be asked about non-member declarations:

    // Translation unit 1:
    extern enum { d, e, f } y;

    // Translation unit 2:
    extern enum { d, e, f } y;

    // Translation unit 3:
    enum { d, e, f } y;

Is this valid C++? Is it valid C?

James Kanze: S::p cannot be defined, because to do so requires a type specifier and the type cannot be named. ::y is valid C because C only requires compatible, not identical, types. In C++, it appears that there is a new type in each declaration, so it would not be valid. This differs from S::x because the unnamed type is part of a named type — but I don't know where or if the Standard says that.

John Max Skaller: It's not valid C++, because the type is a synthesised, unique name for the enumeration type which differs across translation units, as if:

    extern enum _synth1 { d,e,f} y;
    ..
    extern enum _synth2 { d,e,f} y;

had been written.

However, within a class, the ODR implies the types are the same:

    class X { enum { d } y; };

in two translation units ensures that the type of member y is the same: the two X's obey the ODR and so denote the same class, and it follows that there's only one member y and one type that it has.

(See also issues 132 and 216.)




279. Correspondence of "names for linkage purposes"

Section: 3.5  [basic.link]     Status: open     Submitter: Daveed Vandevoorde     Date: 4 Apr 2001

The standard says that an unnamed class or enum definition can be given a "name for linkage purposes" through a typedef. E.g.,

    typedef enum {} E;
    extern E *p;

can appear in multiple translation units.

How about the following combination?

    // Translation unit 1:
    struct S;
    extern S *q;

    // Translation unit 2:
    typedef struct {} S;
    extern S *q;

Is this valid C++?

Also, if the answer is "yes", consider the following slight variant:

    // Translation unit 1:
    struct S {};  // <<-- class has definition
    extern S *q;

    // Translation unit 2:
    typedef struct {} S;
    extern S *q;

Is this a violation of the ODR because two definitions of type S consist of differing token sequences?




338. Enumerator name with linkage used as class name in other translation unit

Section: 3.5  [basic.link]     Status: open     Submitter: Daveed Vandevoorde     Date: 26 Feb 2002

The following declarations are allowed within a translation unit:

  struct S;
  enum { S };

However, 3.5 [basic.link] paragraph 9 seems to say these two declarations cannot appear in two different translation units. That also would mean that the inclusion of a header containing the above in two different translation units is not valid C++.

I suspect this is an oversight and that users should be allowed to have the declarations above appear in different translation units. (It is a fairly common thing to do, I think.)

Mike Miller: I think you meant "enum E { S };" -- enumerators only have external linkage if the enumeration does (3.5 [basic.link] paragraph 4), and 3.5 [basic.link] paragraph 9 only applies to entities with external linkage.

I don't remember why enumerators were given linkage; I don't think it's necessary for mangling non-type template arguments. In any event, I can't think why cross-TU name collisions between enumerators and other entities would cause a problem, so I guess a change here would be okay. I can think of three changes that would have that effect:

  1. Saying that enumerators do not have linkage.
  2. Removing enumerators from the list of entities in the first sentence of 3.5 [basic.link] paragraph 9.
  3. Saying that it's okay for an enumerator in one TU to have the same name as a class type in another TU only if the enumerator hides that same class type in both TUs (the example you gave).

Daveed Vandevoorde: I don't think any of these are sufficient in the sense that the problem isn't limited to enumerators. E.g.:

  struct X;
  extern void X();
shouldn't create cross-TU collisions either.

Mike Miller: So you're saying that cross-TU collisions should only be prohibited if both names denote entities of the same kind (both functions, both objects, both types, etc.), or if they are both references (regardless of what they refer to, presumably)?

Daveed Vandevoorde: Not exactly. Instead, I'm saying that if two entities (with external linkage) can coexist when they're both declared in the same translation unit (TU), then they should also be allowed to coexist when they're declared in two different translation units.

For example:

  int i;
  void i();  // Error
This is an error within a TU, so I don't see a reason to make it valid across TUs.

However, "tag names" (class/struct/union/enum) can sometimes coexist with identically named entities (variables, functions & enumerators, but not namespaces, templates or type names).




2230. Linkage of extern "C" function in unnamed namespace

Section: 3.5  [basic.link]     Status: open     Submitter: Jonathan Wakely     Date: 2016-03-09

[Detailed description pending.]




371. Interleaving of constructor calls

Section: 3.6.2  [basic.start.static]     Status: open     Submitter: Matt Austern     Date: 7 August 2002

Is a compiler allowed to interleave constructor calls when performing dynamic initialization of nonlocal objects? What I mean by interleaving is: beginning to execute a particular constructor, then going off and doing something else, then going back to the original constructor. I can't find anything explicit about this in clause 3.6.2 [basic.start.static].

I'll present a few different examples, some of which get a bit wild. But a lot of what this comes down to is exactly what the standard means when it talks about the order of initialization. If it says that some object x must be initialized before a particular event takes place, does that mean that x's constructor must be entered before that event, or does it mean that it must be exited before that event? If object x must be initialized before object y, does that mean that x's constructor must exit before y's constructor is entered?

(The answer to that question might just be common sense, but I couldn't find an answer in clause 3.6.2 [basic.start.static]. Actually, when I read 3.6.2 [basic.start.static] carefully, I find there are a lot of things I took for granted that aren't there.)

OK, so a few specific scenerios.

  1. We have a translation unit with nonlocal objects A and B, both of which require dynamic initialization. A comes before B. A must be initialized before B. May the compiler start to construct A, get partway through the constructor, then construct B, and then go back to finishing A?
  2. We have a translation unit with nonlocal object A and function f. Construction of A is deferred until after the first statement of main. A must be constructed before the first use of f. Is the compiler permitted to start constructing A, then execute f, then go back to constructing A?
  3. We have nonlocal objects A and B, in two different translation units. The order in which A and B are constructed is unspecified by the Standard. Is the compiler permitted to begin constructing A, then construct B, then finish A's constructor? Note the implications of a 'yes' answer. If A's and B's constructor both call some function f, then the call stack might look like this:
       <runtime gunk>
         <Enter A's constructor>
            <Enter f>
               <runtime gunk>
                  <Enter B's constructor>
                     <Enter f>
                     <Leave f>
                  <Leave B's constructor>
            <Leave f>
         <Leave A's constructor>
    
    The implication of a 'yes' answer for users is that any function called by a constructor, directly or indirectly, must be reentrant.
  4. This last example is to show why a 'no' answer to #3 might be a problem too. New scenerio: we've got one translation unit containing a nonlocal object A and a function f1, and another translation unit containing a nonlocal object B and a function f2. A's constructor calls f2. Initialization of A and B is deferred until after the first statement of main(). Someone in main calls f1. Question: is the compiler permitted to start constructing A, then go off and construct B at some point before f2 gets called, then go back and finish constructing A? In fact, is the compiler required to do that? We've got an unpleasant tension here between the bad implications of a 'yes' answer to #3, and the explicit requirement in 3.6.2 [basic.start.static] paragraph 3.

At this point, you might be thinking we could avoid all of this nonsense by removing compilers' freedom to defer initialization until after the beginning of main(). I'd resist that, for two reasons. First, it would be a huge change to make after the standard has been out. Second, that freedom is necessary if we want to have support for dynamic libraries. I realize we don't yet say anything about dynamic libraries, but I'd hate to make decisions that would make such support even harder.




1659. Initialization order of thread_local template static data members

Section: 3.6.2  [basic.start.static]     Status: open     Submitter: Richard Smith     Date: 2013-04-14

According to 3.6.2 [basic.start.static] paragraph 5,

It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (3.2 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

This doesn't consider that initialization of instantiations of static data members of class templates (which can be thread_local) are unordered. Presumably odr-use of such a static data member should not trigger the initialization of any thread_local variable other than that one?




640. Accessing destroyed local objects of static storage duration

Section: 3.6.3  [basic.start.dynamic]     Status: open     Submitter: Howard Hinnant     Date: 30 July 2007

3.6.3 [basic.start.dynamic] paragraph 2 says,

If a function contains a local object of static storage duration that has been destroyed and the function is called during the destruction of an object with static storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed local object.

I would like to turn this behavior from undefined to well-defined behavior for the purpose of achieving a graceful shutdown, especially in a multi-threaded world.

Background: Alexandrescu describes the “phoenix singleton” in Modern C++ Design. This is a class used as a function local static, that will reconstruct itself, and reapply itself to the atexit chain, if the program attempts to use it after it is destructed in the atexit chain. It achieves this by setting a “destructed flag” in its own state in its destructor. If the object is later accessed (and a member function is called on it), the member function notes the state of the “destructed flag” and does the reconstruction dance. The phoenix singleton pattern was designed to address issues only in single-threaded code where accesses among static objects can have a non-scoped pattern. When we throw in multi-threading, and the possibility that threads can be running after main returns, the chances of accessing a destroyed static significantly increase.

The very least that I would like to see happen is to standardize what I believe is existing practice: When an object is destroyed in the atexit chain, the memory the object occupied is left in whatever state the destructor put it in. If this can only be reliably done for objects with standard layout, that would be an acceptable compromise. This would allow objects to set “I'm destructed” flags in their state and then do something well-defined if accessed, such as throw an exception.

A possible refinement of this idea is to have the compiler set up a 3-state flag around function-local statics instead of the current 2-state flag:

We have the first two states today. We might choose to add the third state, and if execution passes over a function-local static with “destroyed” state, an exception could be thrown. This would mean that we would not have to guarantee memory stability in destroyed objects of static duration.

This refinement would break phoenix singletons, and is not required for the ~mutex()/~condition() I've described and prototyped. But it might make it easier for Joe Coder to apply this kind of guarantee to his own types.




365. Storage duration and temporaries

Section: 3.7  [basic.stc]     Status: open     Submitter: James Kanze     Date: 24 July 2002

There are several problems with 3.7 [basic.stc]:

Steve Adamczyk: There may well be an issue here, but one should bear in mind the difference between storage duration and object lifetime. As far as I can see, there is no particular problem with temporaries having automatic or static storage duration, as appropriate. The point of 12.2 [class.temporary] is that they have an unusual object lifetime.

Notes from Ocrober 2002 meeting:

It might be desirable to shorten the storage duration of temporaries to allow reuse of them. The as-if rule allows some reuse, but such reuse requires analysis, including noting whether the addresses of such temporaries have been taken.

Notes from the August, 2011 meeting:

The CWG decided that further consideration of this issue would be deferred until someone produces a paper explaining the need for action and proposing specific changes.




1682. Overly-restrictive rules on function templates as allocation functions

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: open     Submitter: Jason Merrill     Date: 2009-03-03

Requirements for allocation functions are given in 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 1:

An allocation function can be a function template. Such a template shall declare its return type and first parameter as specified above (that is, template parameter types shall not be used in the return type and first parameter type). Template allocation functions shall have two or more parameters.

There are a couple of problems with this description. First, it is instances of function templates that can be allocation functions, not the templates themselves (cf 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 2, which uses the correct terminology regarding deallocation functions).

More importantly, this specification was written before template metaprogramming was understood and hence prevents use of SFINAE on the return type or parameter type to select among function template specializations. (The parallel passage for deallocation functions in 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 2 shares this deficit.)

(See also issue 1628.)




2207. Alignment of allocation function return value

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: open     Submitter: Richard Smith     Date: 2015-12-02

[Detailed description pending.]




2238. Contradictory alignment requirements for allocation

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: open     Submitter: Jason Merrill     Date: 2016-03-03

Speaking of the value returned by an allocation function, 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 2 says,

The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement

However, the various “Effects” specifications in 18.6.2 [new.delete] have a different formulation:

...allocate size bytes of storage suitably aligned to represent any object of that size.

These should be reconciled.




523. Can a one-past-the-end pointer be invalidated by deleting an adjacent object?

Section: 3.7.4.2  [basic.stc.dynamic.deallocation]     Status: open     Submitter: comp.std.c++     Date: 8 July 2005

When an object is deleted, 3.7.4.2 [basic.stc.dynamic.deallocation] says that the deallocation “[renders] invalid all pointers referring to any part of the deallocated storage.” According to 3.9.2 [basic.compound] paragraph 3, a pointer whose address is one past the end of an array is considered to point to an unrelated object that happens to reside at that address. Does this need to be clarified to specify that the one-past-the-end pointer of an array is not invalidated by deleting the following object? (See also 5.3.5 [expr.delete] paragraph 4, which also mentions that the system deallocation function renders a pointer invalid.)




419. Can cast to virtual base class be done on partially-constructed object?

Section: 3.8  [basic.life]     Status: open     Submitter: Judy Ward     Date: 2 June 2003

Consider

  extern "C" int printf (const char *,...);

  struct Base { Base();};
  struct Derived: virtual public Base {
     Derived() {;}
  };

  Derived d;
  extern Derived& obj = d;

  int i;

  Base::Base() {
    if ((Base *) &obj) i = 4;
    printf ("i=%d\n", i);
  }

  int main() { return 0; }

12.7 [class.cdtor] paragraph 2 makes this valid, but 3.8 [basic.life] paragraph 5 implies that it isn't valid.

Steve Adamczyk: A second issue:

  extern "C" int printf(const char *,...);
  struct A                      { virtual ~A(); int x; };
  struct B : public virtual A   { };
  struct C : public B           { C(int); };
  struct D : public C           { D(); };
 
  int main()                    { D t; printf("passed\n");return 0; }
 
  A::~A()                       {} 
  C::C(int)                     {} 
  D::D() : C(this->x)           {}

Core issue 52 almost, but not quite, says that in evaluating "this->x" you do a cast to the virtual base class A, which would be an error according to 12.7 [class.cdtor] paragraph 2 because the base class B constructor hasn't started yet. 5.2.5 [expr.ref] should be clarified to say that the cast does need to get done.

James Kanze submitted the same issue via comp.std.c++ on 11 July 2003:

Richard Smith: Nonsense. You can use "this" perfectly happily in a constructor, just be careful that (a) you're not using any members that are not fully initialised, and (b) if you're calling virtual functions you know exactly what you're doing.

In practice, and I think in intent, you are right. However, the standard makes some pretty stringent restrictions in 3.8 [basic.life]. To start with, it says (in paragraph 1):

The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when: The lifetime of an object of type T ends when:
(Emphasis added.) Then when we get down to paragraph 5, it says:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [which sounds to me like it would include in the constructor, given the text above] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] If the object will be or was of a non-POD class type, the program has undefined behavior if:

[...]

I can't find any exceptions for the this pointer.

Note that calling a non-static function in the base class, or even constructing the base class in initializer list, involves an implicit conversion of this to a pointer to the base class. Thus undefined behavior. I'm sure that this wasn't the intent, but it would seem to be what this paragraph is saying.




1285. Trivial destructors and object lifetime

Section: 3.8  [basic.life]     Status: open     Submitter: Johannes Schaub     Date: 2011-04-02

The Standard is self-contradictory regarding which destructor calls end the lifetime of an object. 3.8 [basic.life] paragraph 1 says,

The lifetime of an object of type T ends when:

i.e., the lifetime of an object of a class type with a trivial destructor persists until its storage is reused or released. However, 12.4 [class.dtor] paragraph 15 says,

Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8 [basic.life]).

implying that invoking any destructor, even a trivial one, ends the lifetime of the associated object. Similarly, 12.7 [class.cdtor] paragraph 1 says,

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

A similar question arises for pseudo-destructors for non-class types.

Notes from the August, 2011 meeting:

CWG will need a paper exploring this topic before it can act on the issue.




2256. Lifetime of trivially-destructible objects

Section: 3.8  [basic.life]     Status: open     Submitter: Richard Smith     Date: 2016-03-30

[Detailed description pending.]




2258. Storage deallocation during period of destruction

Section: 3.8  [basic.life]     Status: open     Submitter: Richard Smith     Date: 2016-04-12

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The consensus view was that this should be undefined behavior.




350. signed char underlying representation for objects

Section: 3.9  [basic.types]     Status: open     Submitter: Noah Stein     Date: 16 April 2002

Sent in by David Abrahams:

Yes, and to add to this tangent, 3.9.1 [basic.fundamental] paragraph 1 states "Plain char, signed char, and unsigned char are three distinct types." Strangely, 3.9 [basic.types] paragraph 2 talks about how "... the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value." I guess there's no requirement that this copying work properly with signed chars!

Notes from October 2002 meeting:

We should do whatever C99 does. 6.5p6 of the C99 standard says "array of character type", and "character type" includes signed char (6.2.5p15), and 6.5p7 says "character type". But see also 6.2.6.1p4, which mentions (only) an array of unsigned char.

Proposed resolution (April 2003):

Change 3.8 [basic.life] paragraph 5 bullet 3 from

to

Change 3.8 [basic.life] paragraph 6 bullet 3 from

to

Change the beginning of 3.9 [basic.types] paragraph 2 from

For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of char or unsigned char.

to

For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of byte-character type.

Add the indicated text to 3.9.1 [basic.fundamental] paragraph 1:

Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.9 [basic.types]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned byte-character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.

Change 3.10 [basic.lval] paragraph 15 last bullet from

to

Notes from October 2003 meeting:

It appears that in C99 signed char may have padding bits but no trap representation, whereas in C++ signed char has no padding bits but may have -0. A memcpy in C++ would have to copy the array preserving the actual representation and not just the value.

March 2004: The liaisons to the C committee have been asked to tell us whether this change would introduce any unnecessary incompatibilities with C.

Notes from October 2004 meeting:

The C99 Standard appears to be inconsistent in its requirements. For example, 6.2.6.1 paragraph 4 says:

The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value.

On the other hand, 6.2 paragraph 6 says,

If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.

Mike Miller will investigate further.

Proposed resolution (February, 2010):

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

  2. ...The program has undefined behavior if:

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

  4. ...The program has undefined behavior if:

  5. Change 3.9 [basic.types] paragraph 2 as follows:

  6. For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of char or unsigned char a byte-character type (3.9.1 [basic.fundamental]).39 If the content of the that array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example:...
  7. Change 3.9.1 [basic.fundamental] paragraph 1 as follows:

  8. ...Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.11 [basic.align]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned character types unsigned char, all possible bit patterns of the value representation represent numbers...
  9. Change 3.10 [basic.lval] paragraph 15 final bullet as follows:

  10. If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined 52

  11. Change 3.11 [basic.align] paragraph 6 as follows:

  12. The alignment requirement of a complete type can be queried using an alignof expression (5.3.6 [expr.alignof]). Furthermore, the byte-character types (3.9.1 [basic.fundamental]) char, signed char, and unsigned char shall have the weakest alignment requirement. [Note: this enables the byte-character types to be used as the underlying type for an aligned memory area (7.6.2 [dcl.align]). —end note]
  13. Change 5.3.4 [expr.new] paragraph 10 as follows:

  14. ...For arrays of char and unsigned char a byte-character type (3.9.1 [basic.fundamental]), the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (3.11 [basic.align]) of any object type whose size is no greater than the size of the array being created. [Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating byte-character arrays into which objects of other types will later be placed. —end note]

Notes from the March, 2010 meeting:

The CWG was not convinced that there was a need to change the existing specification at this time. Some were concerned that there might be implementation difficulties with giving signed char the requisite semantics; implementations for which that is true can currently make char equivalent to unsigned char and avoid those problems, but the suggested change would undermine that strategy.

Additional note, November, 2014:

There is now the term “narrow character type” that should be used instead of “byte-character type”.




146. Floating-point zero

Section: 3.9.1  [basic.fundamental]     Status: open     Submitter: Andy Sawyer     Date: 23 Jul 1999

3.9.1 [basic.fundamental] does not impose a requirement on the floating point types that there be an exact representation of the value zero. This omission is significant in 4.13 [conv.fctptr] paragraph 1, in which any non-zero value converts to the bool value true.

Suggested resolution: require that all floating point types have an exact representation of the value zero.




251. How many signed integer types are there?

Section: 3.9.1  [basic.fundamental]     Status: open     Submitter: Beman Dawes     Date: 18 Oct 2000

3.9.1 [basic.fundamental] paragraph 2 says that

There are four signed integer types: "signed char", "short int", "int", and "long int."

This would indicate that const int is not a signed integer type.

Notes from the June, 2016 meeting:

See issue 2185.




689. Maximum values of signed and unsigned integers

Section: 3.9.1  [basic.fundamental]     Status: open     Submitter: James Kanze     Date: 30 March, 2008

The relationship between the values representable by corresponding signed and unsigned integer types is not completely described, but 3.9 [basic.types] paragraph 4 says,

The value representation of an object is the set of bits that hold the value of type T.

and 3.9.1 [basic.fundamental] paragraph 3 says,

The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same.

I.e., the maximum value of each unsigned type must be larger than the maximum value of the corresponding signed type.

C90 doesn't have this restriction, and C99 explicitly says (6.2.6.2, paragraph 2),

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M <= N).

Unlike C++, the sign bit is not part of the value, and on an architecture that does not have native support of unsigned types, an implementation can emulate unsigned integers by simply ignoring what would be the sign bit in the signed type and be conforming.

The question is whether we intend to make a conforming implementation on such an architecture impossible. More generally, what range of architectures do we intend to support? And to what degree do we want to follow C99 in its evolution since C89?

(See paper J16/08-0141 = WG21 N2631.)




2185. Cv-qualified numeric types

Section: 3.9.1  [basic.fundamental]     Status: open     Submitter: CWG     Date: 2015-10-21

The definitions of integral, floating, and arithmetic types in 3.9.1 [basic.fundamental] paragraphs 7-8 do not, but presumably should, include cv-qualified versions of those fundamental types.

Notes from the June, 2016 meeting:

This issue subsumes issue 251.




1076. Value categories and lvalue temporaries

Section: 3.10  [basic.lval]     Status: open     Submitter: Daniel Krügler     Date: 2010-06-10

The taxonomy of value categories in 3.10 [basic.lval] classifies temporaries as prvalues. However, some temporaries are explicitly referred to as lvalues (cf 15.1 [except.throw] paragraph 3).




2133. Converting std::nullptr_t to bool

Section: 4.13  [conv.fctptr]     Status: open     Submitter: Richard Smith     Date: 2015-05-28

According to 4.13 [conv.fctptr] paragraph 1,

For direct-initialization (8.6 [dcl.init]), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

The mention of direct-initialization in this context (added by issue 1423) seems odd; standard conversions are on a level below initialization. Should this wording be moved to 8.6 [dcl.init], perhaps as a bullet in paragraph 1?

(See also issue 1781.)




1642. Missing requirements for prvalue operands

Section: 5  [expr]     Status: open     Submitter: Joseph Mansfield     Date: 2013-03-15

Although the note in 3.10 [basic.lval] paragraph 1 states that

The discussion of each built-in operator in Clause 5 [expr] indicates the category of the value it yields and the value categories of the operands it expects

in fact, many of the operators that take prvalue operands do not make that requirement explicit. Possible approaches to address this failure could be a blanket statement that an operand whose value category is not stated is assumed to be a prvalue; adding prvalue requirements to each operand description for which it is missing; or changing the description of the usual arithmetic conversions to state that they imply the lvalue-to-rvalue conversion, which would cover the majority of the omissions.

(See also issue 1685, which deals with an inaccurately-specified value category.)




2249. identifiers and id-expressions

Section: 5.1.4.1  [expr.prim.id.unqual]     Status: open     Submitter: Robert Haberlach     Date: 2016-03-17

According to 5.1.4.1 [expr.prim.id.unqual] paragraph 1,

An identifier is an id-expression provided it has been suitably declared (Clause 7 [dcl.dcl]).

Not only is an identifier an id-expression by (grammatical) definition, declarator-id is defined in terms of id-expression, which makes this circular. If the intention was to disallow use of undeclared identifiers as primary expressions, this should be altered accordingly.




1632. Lambda capture in member initializers

Section: 5.1.5  [expr.prim.lambda]     Status: open     Submitter: Vinny Romano     Date: 2013-03-04

According to 5.1.5 [expr.prim.lambda] paragraph 9,

A lambda-expression whose smallest enclosing scope is a block scope (3.3.3 [basic.scope.block]) is a local lambda expression; any other lambda-expression shall not have a capture-list in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters.

Consequently, lambdas appearing in mem-initializers and brace-or-equal-initializers cannot have a capture-list. However, these expressions are evaluated in the context of the constructor and are permitted to access this and non-static data members.

Should the definition of a local lambda be modified to permit capturing lambdas within these contexts?

Notes from the April, 2013 meeting:

CWG agreed with the intent of this issue.




1822. Lookup of parameter names in lambda-expressions

Section: 5.1.5  [expr.prim.lambda]     Status: open     Submitter: Steve Clamage     Date: 2013-12-10

According to 5.1.5 [expr.prim.lambda] paragraph 7, names appearing in the compound-statement of a lambda-expression are looked up in the context of the lambda-expression, ignoring the fact that the compound-statement will be transformed into the body of the closure type's function operator. This leaves unspecified how the lambda-expression's parameters are found by name lookup. Presumably the parameters hide the corresponding names from the surrounding scope, but this needs to be specified.




2211. Hiding by lambda captures and parameters

Section: 5.1.5  [expr.prim.lambda]     Status: open     Submitter: Ville Voutilainen     Date: 2015-12-07

[Detailed description pending.]




2247. Lambda capture and variable argument list

Section: 5.1.5  [expr.prim.lambda]     Status: open     Submitter: Aaron Ballman     Date: 2016-03-11

[Detailed description pending.]

Notes from the December, 2016 teleconference:

Such examples should have undefined behavior; va_start should only be permitted to access the arguments for the current function.




2215. Redundant description of language linkage in function call

Section: 5.2.2  [expr.call]     Status: open     Submitter: Richard Smith     Date: 2015-12-17

[Detailed description pending.]




2241. Overload resolution is not invoked with a single function

Section: 5.2.2  [expr.call]     Status: open     Submitter: CWG     Date: 2016-03-04

[Detailed description pending.]




943. Is T() a temporary?

Section: 5.2.3  [expr.type.conv]     Status: open     Submitter: Miller     Date: 14 July, 2009

According to 5.2.3 [expr.type.conv] paragraphs 1 and 3 (stated directly or by reference to another section of the Standard), all the following expressions create temporaries:

    T(1)
    T(1, 2)
    T{1}
    T{}

However, paragraph 2 says,

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete effective object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized (8.6 [dcl.init]; no initialization is done for the void() case).

This does not say that the result is a temporary, which means that the lifetime of the result is not specified by 12.2 [class.temporary]. Presumably this is just an oversight.

Notes from the October, 2009 meeting:

The specification in 5.2.3 [expr.type.conv] is in error, not because it fails to state that T() is a temporary but because it requires a temporary for the other cases with fewer than two operands. The case where T is a class type is covered by 12.2 [class.temporary] paragraph 1 (“a conversion that creates an rvalue”), and a temporary should not be created when T is not a class type.




2231. Class member access to static data member template

Section: 5.2.5  [expr.ref]     Status: open     Submitter: Faisal Vali     Date: 2016-02-18

[Detailed description pending.]




742. Postfix increment/decrement with long bit-field operands

Section: 5.2.6  [expr.post.incr]     Status: open     Submitter: Mike Miller     Date: 11 November, 2008

Given the following declarations:

    struct S {
        signed long long sll: 3;
    };
    S s = { -1 };

the expressions s.sll-- < 0u and s.sll < 0u have different results. The reason for this is that s.sll-- is an rvalue of type signed long long (5.2.6 [expr.post.incr]), which means that the usual arithmetic conversions (5 [expr] paragraph 10) convert 0u to signed long long and the result is true. s.sll, on the other hand, is a bit-field lvalue, which is promoted (4.6 [conv.prom] paragraph 3) to int; both operands of < have the same rank, so s.sll is converted to unsigned int to match the type of 0u and the result is false. This disparity seems undesirable.




282. Namespace for extended_type_info

Section: 5.2.8  [expr.typeid]     Status: open     Submitter: Jens Maurer     Date: 01 May 2001

The original proposed resolution for issue 160 included changing extended_type_info (5.2.8 [expr.typeid] paragraph 1, footnote 61) to std::extended_type_info. There was no consensus on whether this name ought to be part of namespace std or in a vendor-specific namespace, so the question was moved into a separate issue.




528. Why are incomplete class types not allowed with typeid?

Section: 5.2.8  [expr.typeid]     Status: open     Submitter: Dave Abrahams     Date: 18 May 2005

5.2.8 [expr.typeid] paragraph 4 says,

When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of the type-id. If the type of the type-id is a reference type, the result of the typeid expression refers to a std::type_info object representing the referenced type. If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined.

I'm wondering whether this is not overly restrictive. I can't think of a reason to require that T be completely-defined in typeid(T) when T is a class type. In fact, several popular compilers enforce that restriction for typeid(T), but not for typeid(T&). Can anyone explain this?

Nathan Sidwell: I think this restriction is so that whenever the compiler has to emit a typeid object of a class type, it knows what the base classes are, and can therefore emit an array of pointers-to-base-class typeids. Such a tree is necessary to implement dynamic_cast and exception catching (in a commonly implemented and obvious manner). If the class could be incomplete, the compiler might have to emit a typeid for incomplete Foo in one object file and a typeid for complete Foo in another object file. The compilation system will then have to make sure that (a) those compare equal and (b) the complete Foo gets priority, if that is applicable.

Unfortunately, there is a problem with exceptions that means there still can be a need to emit typeids for incomplete class. Namely one can throw a pointer-to-pointer-to-incomplete. To implement the matching of pointer-to-derived being caught by pointer-to-base, it is necessary for the typeid of a pointer type to contain a pointer to the typeid of the pointed-to type. In order to do the qualification matching on a multi-level pointer type, one has a chain of pointer typeids that can terminate in the typeid of an incomplete type. You cannot simply NULL-terminate the chain, because one must distinguish between different incomplete types.

Dave Abrahams: So if implementations are still required to be able to do it, for all practical purposes, why aren't we letting the user have the benefits?

Notes from the April, 2006 meeting:

There was some concern expressed that this might be difficult under the IA64 ABI. It was also observed that while it is necessary to handle exceptions involving incomplete types, there is no requirement that the RTTI data structures be used for exception handling.




1954. typeid null dereference check in subexpressions

Section: 5.2.8  [expr.typeid]     Status: open     Submitter: David Majnemer     Date: 2014-06-23

According to 5.2.8 [expr.typeid] paragraph 2,

If the glvalue expression is obtained by applying the unary * operator to a pointer69 and the pointer is a null pointer value (4.11 [conv.ptr]), the typeid expression throws an exception (15.1 [except.throw]) of a type that would match a handler of type std::bad_typeid exception (18.7.4 [bad.typeid]).

The footnote makes clear that this requirement applies without regard to parentheses, but it is unspecified whether it applies when the dereference occurs in a subexpression of the operand (e.g., in the second operand of the comma operator or the second or third operand of a conditional operator). There is implementation divergence on this question.




2048. C-style casts that cast away constness vs static_cast

Section: 5.2.9  [expr.static.cast]     Status: open     Submitter: Richard Smith     Date: 2014-11-19

According to 5.2.9 [expr.static.cast] paragraph 1,

The static_cast operator shall not cast away constness (5.2.11 [expr.const.cast]).

However, this phrasing is problematic in the context of a C-style cast like the following:

   const void *p;
   int *q = (int*)p;

The intent of 5.4 [expr.cast] is that this should be interpreted as a static_cast followed by a const_cast. However, because int* to const void* is a valid standard conversion, and 5.2.9 [expr.static.cast] paragraph 7 allows static_cast to perform the inverse of a standard conversion sequence, the C-style cast is interpreted as just a static_cast without a const_cast and is thus ill-formed.




2243. Incorrect use of implicit conversion sequence

Section: 5.2.9  [expr.static.cast]     Status: open     Submitter: Hubert Tong     Date: 2016-03-08

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The problem is that overload resolution relies on copy initalization and thus does not describe direct initialization. See also issue 1781.




2225. reinterpret_cast to same floating-point type

Section: 5.2.10  [expr.reinterpret.cast]     Status: open     Submitter: Aaron Ballman     Date: 2016-01-14

[Detailed description pending.]




1230. Confusing description of ambiguity of destructor name

Section: 5.3.1  [expr.unary.op]     Status: open     Submitter: Johannes Schaub     Date: 2010-12-13

According to 5.3.1 [expr.unary.op] paragraph 10,

There is an ambiguity in the unary-expression ~X(), where X is a class-name or decltype-specifier. The ambiguity is resolved in favor of treating ~ as a unary complement rather than treating ~X as referring to a destructor.

It is not clear whether this is intended to apply to an expression like (~S)(). In large measure, that depends on whether a class-name is an id-expression or not. If it is, the ambiguity described in 5.3.1 [expr.unary.op] paragraph 10 does apply; if not, the expression is an unambiguous reference to the destructor for class S. There are several places in the Standard that indicate that the name of a type is an id-expression, but that might be more confusing than helpful.




267. Alignment requirement for new-expressions

Section: 5.3.4  [expr.new]     Status: open     Submitter: James Kuyper     Date: 4 Dec 2000

Requirements for the alignment of pointers returned by new-expressions are given in 5.3.4 [expr.new] paragraph 10:

For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the most stringent alignment requirement (3.9 [basic.types]) of any object type whose size is no greater than the size of the array being created.

The intent of this wording is that the pointer returned by the new-expression will be suitably aligned for any data type that might be placed into the allocated storage (since the allocation function is constrained to return a pointer to maximally-aligned storage). However, there is an implicit assumption that each alignment requirement is an integral multiple of all smaller alignment requirements. While this is probably a valid assumption for all real architectures, there's no reason that the Standard should require it.

For example, assume that int has an alignment requirement of 3 bytes and double has an alignment requirement of 4 bytes. The current wording only requires that a buffer that is big enough for an int or a double be aligned on a 4-byte boundary (the more stringent requirement), but that would allow the buffer to be allocated on an 8-byte boundary — which might not be an acceptable location for an int.

Suggested resolution: Change "of any object type" to "of every object type."

A similar assumption can be found in 5.2.10 [expr.reinterpret.cast] paragraph 7:

...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value...

Suggested resolution: Change the wording to

...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T1 are an integer multiple of those of T2) and back to its original type yields the original pointer value...

The same change would also be needed in paragraph 9.




473. Block-scope declarations of allocator functions

Section: 5.3.4  [expr.new]     Status: open     Submitter: John Spicer     Date: 12 Jul 2004

Looking up operator new in a new-expression uses a different mechanism from ordinary lookup. According to 5.3.4 [expr.new] paragraph 9,

If the new-expression begins with a unary :: operator, the allocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class type T or array thereof, the allocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up in the global scope.

Note in particular that the scope in which the new-expression occurs is not considered. For example,

    void f() {
        void* operator new(std::size_t, void*);
        int* i = new int;    // okay?
    }

In this example, the implicit reference to operator new(std::size_t) finds the global declaration, even though the block-scope declaration of operator new with a different signature would hide it from an ordinary reference.

This seems strange; either the block-scope declaration should be ill-formed or it should be found by the lookup.

Notes from October 2004 meeting:

The CWG agreed that the block-scope declaration should not be found by the lookup in a new-expression. It would, however, be found by ordinary lookup if the allocation function were invoked explicitly.




1628. Deallocation function templates

Section: 5.3.4  [expr.new]     Status: open     Submitter: Richard Smith     Date: 2013-02-22

According to 5.3.4 [expr.new] paragraphs 18-20, an exception thrown during the initialization of an object allocated by a new-expression will cause a deallocation function to be called for the object's storage if a matching deallocation function can be found. The rules deal only with functions, however; nothing is said regarding a mechanism by which a deallocation function template might be instantiated to free the storage, although 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 2 indicates that a deallocation function can be an instance of a function template.

One possibility for this processing might be to perform template argument deduction on any deallocation function templates; if there is a specialization that matches the allocation function, by the criteria listed in paragraph 20, that function template would be instantiated and used, although a matching non-template function would take precedence as is the usual outcome of overloading between function template specializations and non-template functions.

Another possibility might be to match non-template deallocation functions with non-template allocation functions and template deallocation functions with template allocation functions.

There is a slightly related wording problem in 5.3.4 [expr.new] paragraph 21:

If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax.

This wording ignores the possibility of default arguments in the allocation function, in which case the arguments passed to the deallocation function might be a superset of those specified in the new-placement.

(See also issue 1682.)




196. Arguments to deallocation functions

Section: 5.3.5  [expr.delete]     Status: open     Submitter: Matt Austern     Date: 20 Jan 2000

5.3.4 [expr.new] paragraph 10 says that the result of an array allocation function and the value of the array new-expression from which it was invoked may be different, allowing for space preceding the array to be used for implementation purposes such as saving the number of elements in the array. However, there is no corresponding description of the relationship between the operand of an array delete-expression and the argument passed to its deallocation function.

3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 does state that

the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.

This statement might be read as requiring an implementation, when processing an array delete-expression and calling the deallocation function, to perform the inverse of the calculation applied to the result of the allocation function to produce the value of the new-expression. (5.3.5 [expr.delete] paragraph 2 requires that the operand of an array delete-expression "be the pointer value which resulted from a previous array new-expression.") However, it is not completely clear whether the "shall" expresses an implementation requirement or a program requirement (or both). Furthermore, there is no direct statement about user-defined deallocation functions.

Suggested resolution: A note should be added to 5.3.5 [expr.delete] to clarify that any offset added in an array new-expression must be subtracted in the array delete-expression.




2239. Sized deallocation with a trivial destructor

Section: 5.3.5  [expr.delete]     Status: open     Submitter: CWG     Date: 2016-03-03

According to 5.3.5 [expr.delete] paragraph 10, deletion of an array of a class with both sized and non-sized deallocation functions is not required to call the sized version if the destructor is trivial:

If deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, the function to be called is selected as follows:

However, if only a sized deallocation function is specified as a class-specific deallocation function, it is not clear how the size argument is to be determined if the class has a trivial destructor.




2087. Left shift of negative value by zero bits

Section: 5.8  [expr.shift]     Status: open     Submitter: Mattheus Izvekov     Date: 2015-02-16

A shift of zero bits should result in the left operand regardless of its sign. However, the current wording of 5.8 [expr.shift] paragraph 2 makes it undefined behavior.

Notes from the February, 2016 meeting:

CWG felt that a reasonable approach might be to define <<N as equivalent to multiplying by 2N in all cases; see also the resolution of issue 1457. The resolution of this question should also be coordinated with SG6 and SG12, as well as with WG14.




2226. Xvalues vs lvalues in conditional expressions

Section: 5.16  [expr.cond]     Status: open     Submitter: Richard Smith     Date: 2016-02-01

[Detailed description pending.]




1256. Unevaluated operands are not necessarily constant expressions

Section: 5.20  [expr.const]     Status: open     Submitter: Nikolay Ivchenkov     Date: 2011-03-08

The current definition of constant expressions appears to make unevaluated operands constant expressions; for example, new char[10] would seem to be a constant expression if it appears as the operand of sizeof. This seems wrong.




2192. Constant expressions and order-of-eval undefined behavior

Section: 5.20  [expr.const]     Status: open     Submitter: Peter Sommerlad     Date: 2015-10-27

Notes from the November, 2016 meeting:

CWG did not wish to require implementations to detect this kind of undefined behavior in determining whether an expression is constant or not, but an implementation should be permitted to reject such expressions. These should be indeterminately sequenced, not unsequenced.




2274. Generic lambda capture vs constexpr if

Section: 6.4.1  [stmt.if]     Status: open     Submitter: Richard Smith     Date: 2016-06-20

[Detailed description pending.]




2123. Omitted constant initialization of local static variables

Section: 6.7  [stmt.dcl]     Status: open     Submitter: Hubert Tong     Date: 2015-02-02

According to 6.7 [stmt.dcl] paragraph 4,

The zero-initialization (8.6 [dcl.init]) of all block-scope variables with static storage duration (3.7.1 [basic.stc.static]) or thread storage duration (3.7.2 [basic.stc.thread]) is performed before any other initialization takes place. Constant initialization (3.6.2 [basic.start.static]) of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered.

The fact that a variable need not be constant-initialized if its block is not entered appears to allow inspection of the variable after zero-initialization but before constant initialization:

  constexpr int x = 0;

  auto foo() {
    constexpr static const int *p = &x;
    struct A {
      const int *const &getPtr() { return p; }
    } a;
    return a;
  }

  int xcpy = *decltype(foo()){ }.getPtr();

  int main(void) {
    return xcpy;
  }

For a related example, consider:

  // tu1.cpp
  extern const int a = 1;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }

  // tu2.cpp
  extern const int a;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }
  int main() {
    return *decltype(f())()();
  }

Here, b may or may not have constant initialization, but we don't have an ODR violation.

If we want to support such code, the nicest option would be to say that the ODR requires us to act as if we pick one of the definitions of the inline function, which requires us to make a consistent choice for all static storage duration variables within a given function. Alternatively, we could say that if multiple definitions of a variable disagree over whether it has constant initialization, then it does not, allowing more implementation simplicity and no functional change outside of pathological cases.

Notes from the February, 2016 meeting:

The second example will be dealt with separately under issue 2242. For the first example, the Standard should require that local types can be used outside their function only via a returned object. It was still to be decided whether this should be undefined behavior or an error on use of such a type. It was also noted that the same issue can arise with static member functions.




157. Omitted typedef declarator

Section: 7  [dcl.dcl]     Status: open     Submitter: Daveed Vandevoorde     Date: 19 Aug 1999

7 [dcl.dcl] paragraph 3 reads,

In a simple-declaration, the optional init-declarator-list can be omitted only when... the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (9.1 [class.name] ), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in those specifiers are among the names being declared by the declaration... In such cases, and except for the declaration of an unnamed bit-field (9.2.4 [class.bit] ), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration. [Example:
    enum { };           // ill-formed
    typedef class { };  // ill-formed
—end example]
In the absence of any explicit restrictions in 7.1.3 [dcl.typedef] , this paragraph appears to allow declarations like the following:
    typedef struct S { };    // no declarator
    typedef enum { e1 };     // no declarator
In fact, the final example in 7 [dcl.dcl] paragraph 3 would seem to indicate that this is intentional: since it is illustrating the requirement that the decl-specifier-seq must introduce a name in declarations in which the init-declarator-list is omitted, presumably the addition of a class name would have made the example well-formed.

On the other hand, there is no good reason to allow such declarations; the only reasonable scenario in which they might occur is a mistake on the programmer's part, and it would be a service to the programmer to require that such errors be diagnosed.




2188. empty-declaration ambiguity

Section: 7  [dcl.dcl]     Status: open     Submitter: Jens Maurer     Date: 2015-10-21

[Detailed description pending.]




498. Storage class specifiers in definitions of class members

Section: 7.1.1  [dcl.stc]     Status: open     Submitter: Matt Austern     Date: 13 Jan 2005

Suppose we've got this class definition:

    struct X {
       void f();
       static int n;
    };

I think I can deduce from the existing standard that the following member definitions are ill-formed:

    static void X::f() { }
    static int X::n;

To come to that conclusion, however, I have to put together several things in different parts of the standard. I would have expected to find an explicit statement of this somewhere; in particular, I would have expected to find it in 7.1.1 [dcl.stc]. I don't see it there, or anywhere.

Gabriel Dos Reis: Or in 3.5 [basic.link] which is about linkage. I would have expected that paragraph to say that that members of class types have external linkage when the enclosing class has an external linkage. Otherwise 3.5 [basic.link] paragraph 8:

Names not covered by these rules have no linkage.

might imply that such members do not have linkage.

Notes from the April, 2005 meeting:

The question about the linkage of class members is already covered by 3.5 [basic.link] paragraph 5.




2232. thread_local anonymous unions

Section: 7.1.1  [dcl.stc]     Status: open     Submitter: Mike Herrick     Date: 2016-02-23

[Detailed description pending.]




1820. Qualified typedef names

Section: 7.1.3  [dcl.typedef]     Status: open     Submitter: Richard Smith     Date: 2013-12-05

The resolution of issue 482 allows a typedef to be redeclared in the same or a containing scope using a qualified declarator-id. This was not the principal goal of the issue and is not supported by current implementations. Should the prohibition of qualified declarator-ids be reinstated for typedefs?




1894. typedef-names and using-declarations

Section: 7.1.3  [dcl.typedef]     Status: open     Submitter: Richard Smith     Date: 2014-03-16

The resolution of issue 407 does not cover cases involving using-declarations. For example:

  namespace A { struct S {}; }
  namespace B {
    // This is valid per issue 407
    using A::S;
    typedef A::S S;
    struct S s;
  }
  namespace C {
    // The typedef does not redefine the name S in this
    // scope, so issue 407's resolution does not apply.
    typedef A::S S;
    using A::S;
    // The name lookup here isn't ambiguous, because it only finds one
    // entity, but it finds both a typedef-name and a non-typedef-name referring
    // to that entity, so the standard doesn't appear to say whether this is valid.
    struct S s;
  }

The same issue appears with using-directives:

  namespace D { typedef A::S S; }
  namespace E {
    using namespace A;
    using namespace D;
    struct S s; // ok? issue 407 doesn't apply here either
  }

One possibility might be to remove the rule that a typedef-name declaration redefines an already-defined name and instead rely on struct stat-style hiding, taking the non-typedef-name if name lookup finds both and they refer to the same type.

Notes from the June, 2014 meeting:

CWG felt that these examples should be well-formed.




2199. Typedefs and tags

Section: 7.1.3  [dcl.typedef]     Status: open     Submitter: Richard Smith     Date: 2015-11-12

[Detailed description pending.]




2212. Typedef changing linkage after use

Section: 7.1.3  [dcl.typedef]     Status: open     Submitter: Richard Smith     Date: 2015-12-09

[Detailed description pending.]




2217. constexpr constructors for non-literal types

Section: 7.1.5  [dcl.constexpr]     Status: open     Submitter: Daveed Vandevoorde     Date: 2015-12-21

Given an example like:

  struct S {
    constexpr S(): i(42) {  }
    ~S();
    int i;
  };
  double x[S().i];  // Error

such a constexpr constructor is completely useless, but there doesn't appear to be anything in the current wording making it ill-formed. Should it be?




2268. Unions with mutable members in constant expressions revisited

Section: 7.1.5  [dcl.constexpr]     Status: open     Submitter: Richard Smith     Date: 2016-05-26

[Detailed description pending.]




2195. Unsolicited reading of trailing volatile members

Section: 7.1.7.1  [dcl.type.cv]     Status: open     Submitter: Hubert Tong     Date: 2015-11-06

[Detailed description pending.]




144. Position of friend specifier

Section: 7.1.7.3  [dcl.type.elab]     Status: open     Submitter: Daveed Vandevoorde     Date: 22 Jul 1999

7.1.7.3 [dcl.type.elab] paragraph 1 seems to impose an ordering constraint on the elements of friend class declarations. However, the general rule is that declaration specifiers can appear in any order. Should

    class C friend;
be well-formed?


2213. Forward declaration of partial specializations

Section: 7.1.7.3  [dcl.type.elab]     Status: open     Submitter: Richard Smith     Date: 2015-12-11

[Detailed description pending.]




36. using-declarations in multiple-declaration contexts

Section: 7.3.3  [namespace.udecl]     Status: open     Submitter: Andrew Koenig     Date: 20 Aug 1998

Section 7.3.3 [namespace.udecl] paragraph 8 says:

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.
It contains the following example:
    namespace A {
            int i;
    }
    
    namespace A1 {
            using A::i;
            using A::i;             // OK: double declaration
    }
    
    void f()
    {
            using A::i;
            using A::i;             // error: double declaration
    }
However, if "using A::i;" is really a declaration, and not a definition, it is far from clear that repeating it should be an error in either context. Consider:
    namespace A {
            int i;
            void g();
    }
    
    void f() {
            using A::g;
            using A::g;
    }
Surely the definition of f should be analogous to
    void f() {
            void g();
            void g();
    }
which is well-formed because "void g();" is a declaration and not a definition.

Indeed, if the double using-declaration for A::i is prohibited in f, why should it be allowed in namespace A1?

Proposed Resolution (04/99): Change the comment "// error: double declaration" to "// OK: double declaration". (This should be reviewed against existing practice.)

Notes from 04/00 meeting:

The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-declarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.

As a result of the lack of agreement, the issue was returned to "open" status.

See also issues 56, 85, and 138..

Additional notes (January, 2005):

Some related issues have been raised concerning the following example (modified from a C++ validation suite test):

    struct A
    {
        int i;
        static int j;
    };

    struct B : A { };
    struct C : A { };

    struct D : virtual B, virtual C
    {
        using B::i;
        using C::i;
        using B::j;
        using C::j;
    };

Currently, it appears that the using-declarations of i are ill-formed, on the basis of 7.3.3 [namespace.udecl] paragraph 10:

Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (3.3 [basic.scope]) also apply to using-declarations.

Because the using-declarations of i refer to different objects, declaring them in the same scope is not permitted under 3.3 [basic.scope]. It might, however, be preferable to treat this case as many other ambiguities are: allow the declaration but make the program ill-formed if a name reference resolves to the ambiguous declarations.

The status of the using-declarations of j, however, is less clear. They both declare the same entity and thus do not violate the rules of 3.3 [basic.scope]. This might (or might not) violate the restrictions of 9.2 [class.mem] paragraph 1:

Except when used to declare friends (11.3 [class.friend]) or to introduce the name of a member of a base class into a derived class (7.3.3 [namespace.udecl], _N3225_.11.3 [class.access.dcl]), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class. A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.

Do the using-declarations of j repeatedly declare the same member? Or is the preceding sentence an indication that a using-declaration is not a declaration of a member?




813. typename in a using-declaration with a non-dependent name

Section: 7.3.3  [namespace.udecl]     Status: open     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 101

7.3.3 [namespace.udecl] paragraph 20 says,

If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2 [temp.dep]), the name introduced by the using-declaration is treated as a typedef-name (7.1.3 [dcl.typedef]).

This wording does not address use of typename in a using-declaration with a non-dependent name; the primary specification of the typename keyword in 14.6 [temp.res] does not appear to describe this case, either.




852. using-declarations and dependent base classes

Section: 7.3.3  [namespace.udecl]     Status: open     Submitter: Michael Wong     Date: 2 April, 2009

The status of an example like the following is unclear in the current Standard:

    struct B {
        void f();
    };
    template<typename T> struct S: T {
        using B::f;
    };

7.3.3 [namespace.udecl] does not deal explicitly with dependent base classes, but does say in paragraph 3,

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. If such a using-declaration names a constructor, the nested-name-specier shall name a direct base class of the class being defined; otherwise it introduces the set of declarations found by member name lookup (10.2 [class.member.lookup], 3.4.3.1 [class.qual]).

In the definition of S, B::f is not a dependent name but resolves to an apparently unrelated class. However, because S could be instantiated as S<B>, presumably 14.6 [temp.res] paragraph 8 would apply:

No diagnostic shall be issued for a template definition for which a valid specialization can be generated.

Note also the resolution of issue 515, which permitted a similar use of a dependent base class named with a non-dependent name.




1742. using-declarations and scoped enumerators

Section: 7.3.3  [namespace.udecl]     Status: open     Submitter: Richard Smith     Date: 2013-08-28

A using-declaration cannot name a scoped enumerator, according to 7.3.3 [namespace.udecl] paragraph 7. This is presumably because a scoped enumerator belongs to an enumeration scope and thus logically cannot belong to the non-enumeration scope in which the using-declaration appears. It seems inconsistent, however, to permit using-declarations to name unscoped enumerators but not scoped enumerators.

Also, 7.3.3 [namespace.udecl] paragraph 3 says,

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

The consequence of this is that

  enum E { e0 };
  void f() {
    using E::e0;
  }

is well-formed, but

  struct B {
    enum E { e0 };
  };
  struct D : B {
    using B::E::e0;
  };

is not. Again, this seems inconsistent. Should these rules be relaxed?




563. Linkage specification for objects

Section: 7.5  [dcl.link]     Status: open     Submitter: Daveed Vandevoorde     Date: 8 March 2006

It is not clear whether some of the wording in 7.5 [dcl.link] that applies only to function types and names ought also to apply to object names. In particular, paragraph 3 says,

Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++".

Nothing is said about variable names, apparently meaning that implementations need not provide C (or even C++!) linkage for variable names. Also, paragraph 5 says,

Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.

There doesn't seem to be a good reason for these provisions not to apply to variable names, as well.




1818. Visibility and inherited language linkage

Section: 7.5  [dcl.link]     Status: open     Submitter: Richard Smith     Date: 2013-12-04

Does the language linkage of a block-scope declaration determine the language linkage of a subsequent declaration of the same name in a different scope? For example,

   extern "C" void f() {
     void g();    // Implicitly extern "C"
   }
   void g() { }   // Also extern "C" or linkage mismatch?

In other contexts, inheritance of linkage requires that the earlier declaration be visible, as in 3.5 [basic.link] paragraph 6:

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration.

The specification for language linkage in 7.5 [dcl.link] paragraph 5, however, makes no mention of visibility:

A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.



1617. alignas and non-defining declarations

Section: 7.6.2  [dcl.align]     Status: open     Submitter: Richard Smith     Date: 2012-02-02

According to 7.6.2 [dcl.align] paragraph 6,

If the defining declaration of an entity has an alignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have no alignment-specifier. Conversely, if any declaration of an entity has an alignment-specifier, every defining declaration of that entity shall specify an equivalent alignment. No diagnostic is required if declarations of an entity have different alignment-specifiers in different translation units.

Because this is phrased in terms of the definition of an entity, an example like the following is presumably well-formed (even though there can be no definition of n):

   alignas(8) extern int n;
   alignas(16) extern int n;

Is this intentional?




2223. Multiple alignas specifiers

Section: 7.6.2  [dcl.align]     Status: open     Submitter: Mike Herrick     Date: 2016-01-12

[Detailed description pending.]




2228. Ambiguity resolution for cast to function type

Section: 8.2  [dcl.ambig.res]     Status: open     Submitter: Richard Smith     Date: 2016-02-02

[Detailed description pending.]




504. Should use of a variable in its own initializer require a diagnostic?

Section: 8.3.2  [dcl.ref]     Status: open     Submitter: Bjarne Stroustrup     Date: 14 Apr 2005

Split off from issue 453.

It is in general not possible to determine at compile time whether a reference is used before it is initialized. Nevertheless, there is some sentiment to require a diagnostic in the obvious cases that can be detected at compile time, such as the name of a reference appearing in its own initializer. The resolution of issue 453 originally made such uses ill-formed, but the CWG decided that this question should be a separate issue.

Rationale (October, 2005):

The CWG felt that this error was not likely to arise very often in practice. Implementations can warn about such constructs, and the resolution for issue 453 makes executing such code undefined behavior; that seemed to address the situation adequately.

Note (February, 2006):

Recent discussions have suggested that undefined behavior be reduced. One possibility (broadening the scope of this issue to include object declarations as well as references) was to require a diagnostic if the initializer uses the value, but not just the address, of the object or reference being declared:

    int i = i;        // Ill-formed, diagnostic required
    void* p = &p;     // Okay



361. Forward reference to default argument

Section: 8.3.6  [dcl.fct.default]     Status: open     Submitter: Steve Clamage     Date: 17 June 2002

Is this program well-formed?

  struct S {
    static int f2(int = f1()); // OK?
    static int f1(int = 2);
  };
  int main()
  {
    return S::f2();
  }

A class member function can in general refer to class members that are declared lexically later. But what about referring to default arguments of member functions that haven't yet been declared?

It seems to me that if f2 can refer to f1, it can also refer to the default argument of f1, but at least one compiler disagrees.

Notes from the February, 2012 meeting:

Implementations seem to have come to agreement that this example is ill-formed.

Additional note (March, 2013):

Additional discussion has occurred suggesting the following examples as illustrations of this issue:

  struct B {
   struct A { int a = 0; };
   B(A = A());    // Not permitted?
  };

as well as

  struct C {
   struct A { int a = C().n; }; // can we use the default argument here?
   C(int k = 0);
   int n;
  };

  bool f();
  struct D {
   struct A { bool a = noexcept(B()); }; // can we use the default initializer here?
   struct B { int b = f() ? throw 0 : 0; };
  };

(See also issue 325.)

Additional note (October, 2013):

Issue 1330 treats exception-specifications like default arguments, evaluated in the completed class type. That raises the same questions regarding self-referential noexcept clauses that apply to default arguments.




1609. Default arguments and function parameter packs

Section: 8.3.6  [dcl.fct.default]     Status: open     Submitter: Jonathan Caves     Date: 2013-01-25

It is not clear from 8.3.6 [dcl.fct.default] whether the following is well-formed or not:

  template<typename... T>
  void f2(int a = 0, T... b, int c = 1);

  f2<>(); // parameter a has the value 0 and parameter c has the value 1

(T... b is a non-deduced context per 14.8.2.5 [temp.deduct.type] paragraph 5, so the template arguments must be specified explicitly.)

Notes from the April, 2013 meeting:

CWG agreed that the example should be ill-formed.

Additional note (August, 2013):

8.3.6 [dcl.fct.default] paragraph 4 explicitly allows for a function parameter pack to follow a parameter with a default argument:

In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack.

However, any instantiation of such a function template with a non-empty pack expansion would result in a function declaration in which one or more parameters without default arguments (from the pack expansion) would follow a parameter with a default argument and thus would be ill-formed. Such a function template declaration thus violates 14.6 [temp.res] paragraph 8:

If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

Although the drafting review teleconference of 2013-08-26 suggested closing the issue as NAD, it is being kept open to discuss and resolve this apparent contradiction.

Notes from the September, 2013 meeting:

CWG agreed that this example should be accepted; the restriction on default arguments applies to the template declaration itself, not to its specializations.




2233. Function parameter packs following default arguments

Section: 8.3.6  [dcl.fct.default]     Status: open     Submitter: Richard Smith     Date: 2016-02-25

[Detailed description pending.]




2221. Copying volatile objects

Section: 8.4.2  [dcl.fct.def.default]     Status: open     Submitter: 2016-01-09     Date: Vinny Romano

[Detailed description pending.]




670. Copy initialization via derived-to-base conversion in the second step

Section: 8.6  [dcl.init]     Status: open     Submitter: Jason Merrill     Date: 20 December 2007

In this example:

    struct A {};

    struct B: A {
       B(int);
       B(B&);
       B(A);
    };

    void foo(B);

    void bar() {
       foo(0);
    }

we are copy-initializing a B from 0. So by 13.3.1.4 [over.match.copy] we consider all the converting constructors of B, and choose B(int) to create a B. Then, by 8.6 [dcl.init] paragraph 15, we direct-initialize the parameter from that temporary B. By 13.3.1.3 [over.match.ctor] we consider all constructors. The copy constructor cannot be called with a temporary, but B(A) is callable.

As far as I can tell, the Standard says that this example is well-formed, and calls B(A). EDG and G++ have rejected this example with a message about the copy constructor not being callable, but I have been unsuccessful in finding anything in the Standard that says that we only consider the copy constructor in the second step of copy-initialization. I wouldn't mind such a rule, but it doesn't seem to be there. And implementing issue 391 causes G++ to start accepting the example.

This question came up before in a GCC bug report; in the discussion of that bug Nathan Sidwell said that some EDG folks explained to him why the testcase is ill-formed, but unfortunately didn't provide that explanation in the bug report.

I think the resolution of issue 391 makes this example well-formed; it was previously ill-formed because in order to bind the temporary B(0) to the argument of A(const A&) we needed to make another temporary B, and that's what made the example ill-formed. If we want this example to stay ill-formed, we need to change something else.

Steve Adamczyk:

I tracked down my response to Nathan at the time, and it related to my paper N1232 (on the auto_ptr problem). The change that came out of that paper is in 13.3.3.1 [over.best.ics] paragraph 4:

However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv], or 13.3.1.6 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

This is intended to prevent use of more than one implicit user- defined conversion in an initialization.

I told Nathan B(A) can't be called because its argument would require yet another user-defined conversion, but I was wrong. I saw the conversion from B to A and immediately thought “user-defined,” but in fact because B is a derived class of A the conversion according to 13.3.3.1 [over.best.ics] paragraph 6 is a derived-to-base Conversion (even though it will be implemented by calling a copy constructor).

So I agree with you: with the analysis above and the change for issue 391 this example is well-formed. We should discuss whether we want to make a change to keep it ill-formed.




2196. Zero-initialization with virtual base classes

Section: 8.6  [dcl.init]     Status: open     Submitter: Richard Smith     Date: 2015-11-06

[Detailed description pending.]




2269. Additional recursive references in aggregate DMIs

Section: 8.6.1  [dcl.init.aggr]     Status: open     Submitter: Vinny Romano     Date: 2016-06-07

[Detailed description pending.]




2272. Implicit initialization of aggregate members of reference type

Section: 8.6.1  [dcl.init.aggr]     Status: open     Submitter: Vinny Romano     Date: 2016-06-10

[Detailed description pending.]




233. References vs pointers in UDC overload resolution

Section: 8.6.3  [dcl.init.ref]     Status: open     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.6.3 [dcl.init.ref] and 4.5 [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.5 [conv.qual]. According to 8.6.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.6.3 [dcl.init.ref] paragraph 6, 4.5 [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.




2267. Copy-initialization of temporary in reference direct-initialization

Section: 8.6.3  [dcl.init.ref]     Status: open     Submitter: Richard Smith     Date: 2016-05-25

[Detailed description pending.]




1599. Lifetime of initializer_list underlying array

Section: 8.6.4  [dcl.init.list]     Status: open     Submitter: Johannes Schaub     Date: 2012-02-10

The normative wording of 8.6.4 [dcl.init.list] regarding the lifetime of the array underlying an initializer_list object does not match the intent as specified in the example in paragraph 6 of that section, even after application of the resolution of issue 1290. That example contains the lines:

  void f() {
    std::initializer_list<int> i3 = { 1, 2, 3 };
  }

The commentary indicates that the lifetime of the array created for the initialization of i3 “persists for the lifetime of the variable.” However, that is not the effect of the normative wording. According to paragraph 3,

if T is a specialization of std::initializer_list<E>, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (8.6 [dcl.init]).

In other words, the underlying array for {1,2,3} in the example is associated with the temporary and shares its lifetime; its lifetime is not extended to that of the variable.

(See also issue 1565.)

Notes from the February, 2014 meeting:

The resolution of issue 1299, clarifying the relationship between temporary expressions and temporary objects, is relevant to this issue.




2168. Narrowing conversions and +/- infinity

Section: 8.6.4  [dcl.init.list]     Status: open     Submitter: Hubert Tong     Date: 2015-08-19

The intended treatment of a floating point infinity with respect to narrowing conversions is not clear. Is std::numeric_limits<double>::infinity() usable in a constant expression, for example, and should that be different from a calculation that results in an infinity?

Notes from the October, 2015 meeting:

CWG requests the assistance of SG6 in resolving this issue.

Notes from the November, 2016 meeting:

SG6 said that arithmetic operations (not conversions) that produce infinity are not allowed in a constant expression. However, using std::numeric_limits<T>::infinity() is okay, but it can't be used as a subexpression. Conversions that produce infinity from non-infinity values are considered to be narrowing conversions.




2252. Enumeration list-initialization from the same type

Section: 8.6.4  [dcl.init.list]     Status: open     Submitter: Richard Smith     Date: 2016-03-22

[Detailed description pending.]




511. POD-structs with template assignment operators

Section: 9  [class]     Status: open     Submitter: Alisdair Meredith     Date: 19 Mar 2005

A POD-struct is not permitted to have a user-declared copy assignment operator (9 [class] paragraph 4). However, a template assignment operator is not considered a copy assignment operator, even though its specializations can be selected by overload resolution for performing copy operations (12.8 [class.copy] paragraph 9 and especially footnote 114). Consequently, X in the following code is a POD, notwithstanding the fact that copy assignment (for a non-const operand) is a member function call rather than a bitwise copy:

    struct X {
      template<typename T> const X& operator=(T&);
    };
    void f() {
      X x1, x2;
      x1 = x2;  // calls X::operator=<X>(X&)
    }

Is this intentional?




2234. Missing rules for simple-template-id as class-name

Section: 9  [class]     Status: open     Submitter: Richard Smith     Date: 2016-02-25

[Detailed description pending.]




1821. Qualified redeclarations in a class member-specification

Section: 9.2  [class.mem]     Status: open     Submitter: Richard Smith     Date: 2013-1205

9.2 [class.mem] paragraph 1 allows nested classes, class templates, and enumerations to be declared and then later defined in the class member-specification. There does not appear to be a restriction on using a qualified-id in that definition. Should such a restriction be added?




2254. Standard-layout classes and unnamed bit-fields

Section: 9.2  [class.mem]     Status: open     Submitter: Richard Smith     Date: 2016-03-23

[Detailed description pending.]




1943. Unspecified meaning of “bit”

Section: 9.2.4  [class.bit]     Status: open     Submitter: Hubert Tong     Date: 2014-02-13

CWG decided at the 2014-06 (Rapperswil) meeting to address only a limited subset of the questions raised by issues 1857 and 1861. This issue is a placeholder for the remaining questions, such as defining a “bit” in terms of a value of 2n, specifying whether a bit-field has a sign bit, etc.




2229. Volatile unnamed bit-fields

Section: 9.2.4  [class.bit]     Status: open     Submitter: David Majnemer     Date: 2016-02-08

[Detailed description pending.]




2253. Unnamed bit-fields and zero-initialization

Section: 9.2.4  [class.bit]     Status: open     Submitter: Aaron Ballman     Date: 2016-03-23

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The consensus was that unnamed bit-fields do constitute padding; more generally, padding should be normatively defined, along the lines suggested in 9.2.4 [class.bit] paragraphs 1-2.




57. Empty unions

Section: 9.3  [class.union]     Status: open     Submitter: Steve Adamczyk     Date: 13 Oct 1998

There doesn't seem to be a prohibition in 9.3 [class.union] against a declaration like

    union { int : 0; } x;
Should that be valid? If so, 8.6 [dcl.init] paragraph 5 third bullet, which deals with default-initialization of unions, should say that no initialization is done if there are no data members.

What about:

    union { } x;
    static union { };
If the first example is well-formed, should either or both of these cases be well-formed as well?

(See also the resolution for issue 151.)

Notes from 10/00 meeting: The resolution to issue 178, which was accepted as a DR, addresses the first point above (default initialization). The other questions have not yet been decided, however.




380. Definition of "ambiguous base class" missing

Section: 10.2  [class.member.lookup]     Status: open     Submitter: Jason Merrill     Date: 22 Oct 2002

The term "ambiguous base class" doesn't seem to be actually defined anywhere. 10.2 [class.member.lookup] paragraph 7 seems like the place to do it.




600. Does access control apply to members or to names?

Section: 11  [class.access]     Status: open     Submitter: Alisdair Meredith     Date: 3 October 2006

Referring to a private member of a class, 11 [class.access] paragraph 1 says,

its name can be used only by members and friends of the class in which it is declared.

That wording does not appear to reflect the intent of access control, however. Consider the following:

    struct S {
        void f(int);
    private:
        void f(double);
    };

    void g(S* sp) {
        sp->f(2);        // Ill-formed?
    }

The statement from 11 [class.access] paragraph 1 says that the name f can be used only by members and friends of S. Function g is neither, and it clearly contains a use of the name f. That appears to make it ill-formed, in spite of the fact that overload resolution will select the public member.

A related question is whether the use of the term “name” in the description of the effect of access control means that it does not apply to constructors and destructors, which do not have names.

Mike Miller: The phrase “its name can be used” should be understood as “it can be referred to by name.” Paragraph 4, among other places, makes it clear that access control is applied after overload resolution. The “name” phrasing is there to indicate that access control does not apply where the name is not used (in a call via a pointer, for example).




360. Using-declaration that reduces access

Section: 11.2  [class.access.base]     Status: open     Submitter: Steve Clamage     Date: 4 June 2002

I have heard a claim that the following code is valid, but I don't see why.

  struct A {
    int foo ();
  };

  struct B: A {
  private:
    using A::foo;
  };

  int main ()
  {
    return B ().foo ();
  }

It seems to me that the using declaration in B should hide the public foo in A. Then the call to B::foo should fail because B::foo is not accessible in main.

Am I missing something?

Steve Adamczyk: This is similar to the last example in 11.2 [class.access.base]. In prose, the rule is that if you have access to cast to a base class and you have access to the member in the base class, you are given access in the derived class. In this case, A is a public base class of B and foo is public in A, so you can access foo through a B object. The actual permission for this is in the fourth bullet in 11.2 [class.access.base] paragraph 4.

The wording changes for issue 9 make this clearer, but I believe even without them this example could be discerned to be valid.

See my paper J16/96-0034, WG21/N0852 on this topic.

Steve Clamage: But a using-declaration is a declaration (7.3.3 [namespace.udecl]). Compare with

  struct B : A {
  private:
    int foo();
  };

In this case, the call would certainly be invalid, even though your argument about casting B to an A would make it OK. Your argument basically says that an access adjustment to make something less accessible has no effect. That also doesn't sound right.

Steve Adamczyk: I agree that is strange. I do think that's what 11.2 [class.access.base] says, but perhaps that's not what we want it to say.




2246. Access of indirect virtual base class constructors

Section: 11.2  [class.access.base]     Status: open     Submitter: Vinny Romano     Date: 2016-03-08

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The injected-class-name is irrelevant to the example, which is ill-formed. The access should be permitted only of conversion of the this pointer to a pointer to the base class would succeed.




718. Non-class, non-function friend declarations

Section: 11.3  [class.friend]     Status: open     Submitter: John Spicer     Date: 18 September, 2008

With the change from a scope-based to an entity-based definition of friendship (see issues 372 and 580), it could well make sense to grant friendship to enumerations and variables, for example:

    enum E: int;
    class C {
      static const int i = 5;  // Private
      friend E;
      friend int x;
    };
    enum E { e = C::i; };      // OK: E is a friend
    int x = C::i;              // OK: x is a friend

According to the current wording of 11.3 [class.friend] paragraph 3, the friend declaration of E is well-formed but ignored, while the friend declaration of x is ill-formed.




2187. Protected members and access via qualified-id

Section: 11.4  [class.protected]     Status: open     Submitter: Hubert Tong     Date: 2015-10-16

[Detailed description pending.]




2244. Base class access in aggregate initialization

Section: 11.4  [class.protected]     Status: open     Submitter: Richard Smith     Date: 2016-03-08

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The consensus favored accepting f and g while rejecting h.




581. Can a templated constructor be explicitly instantiated or specialized?

Section: 12.1  [class.ctor]     Status: open     Submitter: Mark Mitchell     Date: 19 May 2006

Although it is not possible to specify a constructor's template arguments in a constructor invocation (because the constructor has no name but is invoked by use of the constructor's class's name), it is possible to “name” the constructor in declarative contexts: per 3.4.3.1 [class.qual] paragraph 2,

In a lookup in which the constructor is an acceptable lookup result, if the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9 [class]), the name is instead considered to name the constructor of class C... Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor.

Should it therefore be possible to specify template-arguments for a templated constructor in an explicit instantiation or specialization? For example,

    template <int dim> struct T {};
    struct X {
      template <int dim> X (T<dim> &) {};
    };

    template X::X<> (T<2> &);

If so, that should be clarified in the text. In particular, 12.1 [class.ctor] paragraph 1 says,

Constructors do not have names. A special declarator syntax using an optional sequence of function-specifiers (7.1.2 [dcl.fct.spec]) followed by the constructor's class name followed by a parameter list is used to declare or define the constructor.

This certainly sounds as if the parameter list must immediately follow the class name, with no allowance for a template argument list.

It would be worthwhile in any event to revise this wording to utilize the “considered to name” approach of 3.4.3.1 [class.qual]; as it stands, this wording sounds as if the following would be acceptable:

    struct S {
        S();
    };
    S() { }    // qualified-id not required?

Notes from the October, 2006 meeting:

It was observed that explicitly specifying the template arguments in a constructor declaration is never actually necessary because the arguments are, by definition, all deducible and can thus be omitted.




2237. Can a template-id name a constructor?

Section: 12.1  [class.ctor]     Status: open     Submitter: Faisal Vali     Date: 2016-03-02

[Detailed description pending.]




2273. Inheriting constructors vs implicit default constructor

Section: 12.1  [class.ctor]     Status: open     Submitter: Richard Smith     Date: 2016-06-17

[Detailed description pending.]




2257. Lifetime extension of references vs exceptions

Section: 12.2  [class.temporary]     Status: open     Submitter: Hubert Tong     Date: 2016-04-07

[Detailed description pending.]

See also issue 1634.

Notes from the December, 2016 teleconference:

The consensus was that the temporaries should be destroyed immediately if an exception occurs. 15.2 [except.ctor] paragraph 3 should be extended to apply to static initialization, so that even if a temporary is lifetime-extended (because it has static storage duration), it will be destroyed in case of an exception.




1969. Missing exclusion of ~S as an ordinary function name

Section: 12.4  [class.dtor]     Status: open     Submitter: Richard Smith     Date: 2014-07-14

There does not appear to be wording to exclude use of a name like ~S for entities other than destructors.




607. Lookup of mem-initializer-ids

Section: 12.6.2  [class.base.init]     Status: open     Submitter: Richard Corden     Date: 5 December 2006

In an example like,

    struct Y {};

    template <typename T>
    struct X : public virtual Y { };

    template <typename T>
    class A : public X<T> {
      template <typename S>
      A (S)
        : S ()
      {
      }
    };

    template A<int>::A (Y);

Should S be found? (S is a dependent name, so if it resolves to a base class type in the instantiated template, it should satisfy the requirements.) All the compilers I tried allowed this example, but 12.6.2 [class.base.init] paragraph 2 says,

Names in a mem-initializer-id are looked up in the scope of the constructor's class and, if not found in that scope, are looked up in the scope containing the constructor's definition.

The name S is not declared in those scopes.

Mike Miller: Here's another example that is accepted by most/all compilers but not by the current wording:

    namespace N {
      struct B { B(int); };
      typedef B typedef_B;
      struct D: B {
        D();
      };
    }

    N::D::D(): typedef_B(0) { }

Except for the fact that the constructor function parameter names are ignored (see paragraph 7), what the compilers seem to be doing is essentially ordinary unqualified name lookup.

Notes from the October, 2009 meeting:

The eventual resolution of this issue should take into account the template parameter scope introduced by the resolution of issue 481.




2204. Naming delegated constructors

Section: 12.6.2  [class.base.init]     Status: open     Submitter: Hubert Tnog     Date: 2015-11-25

[Detailed description pending.]




2227. Destructor access and default member initializers

Section: 12.6.2  [class.base.init]     Status: open     Submitter: Richard Smith     Date: 2016-02-01

[Detailed description pending.]




6. Should the optimization that allows a class object to alias another object also allow the case of a parameter in an inline function to alias its argument?

Section: 12.8  [class.copy]     Status: open     Submitter: unknown     Date: unknown

[Picked up by evolution group at October 2002 meeting.]

(See also paper J16/99-0005 = WG21 N1182.)

At the London meeting, 12.8 [class.copy] paragraph 31 was changed to limit the optimization described to only the following cases:

One other case was deemed desirable as well: However, there are cases when this aliasing was deemed undesirable and, at the London meeting, the committee was not able to clearly delimit which cases should be allowed and which ones should be prohibited.

Can we find an appropriate description for the desired cases?

Rationale (04/99): The absence of this optimization does not constitute a defect in the Standard, although the proposed resolution in the paper should be considered when the Standard is revised.

Note (March, 2008):

The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action (not for C++0x). See paper J16/07-0033 = WG21 N2173.

Notes from the June, 2008 meeting:

The CWG decided to take no action on this issue until an interested party produces a paper with analysis and a proposal.




1049. Copy elision through reference parameters of inline functions

Section: 12.8  [class.copy]     Status: open     Submitter: Jason Merrill     Date: 2010-03-10

Consider the following example:

    int c;

    struct A {
       A() { ++c; }
       A(const A&) { ++c; }
    };

    struct B {
       A a;
       B(const A& a): a(a) { }
    };

    int main() {
       (B(A()));
       return c - 1;
    }

Here we would like to be able to avoid the copy and just construct the A() directly into the A subobject of B. But we can't, because it isn't allowed by 12.8 [class.copy] paragraph 34 bullet 3:

The part about not being bound to a reference was added for an unrelated reason by issue 185. If that resolution were recast to require that the temporary object is not accessed after the copy, rather than banning the reference binding, this optimization could be applied.

The similar example using pass by value is also not one of the allowed cases, which could be considered part of issue 6.




2197. Overload resolution and deleted special member functions

Section: 12.8  [class.copy]     Status: open     Submitter: Maxim Kartashev     Date: 2015-11-11

[Detailed description pending.]




2203. Defaulted copy/move constructors and UDCs

Section: 12.8  [class.copy]     Status: open     Submitter: Vinny Romano     Date: 2015-11-20

[Detailed description pending.]




2264. Memberwise copying with indeterminate value

Section: 12.8  [class.copy]     Status: open     Submitter: Hubert Tong     Date: 2016-05-06

[Detailed description pending.]




2189. Surrogate call template

Section: 13.3.1.1.2  [over.call.object]     Status: open     Submitter: Jason Merrill     Date: 2015-10-22

[Detailed description pending.]




545. User-defined conversions and built-in operator overload resolution

Section: 13.3.1.2  [over.match.oper]     Status: open     Submitter: Steve Clamage     Date: 31 October 2005

Consider the following example:

    class B1 {};
    typedef void (B1::*PB1) (); // memptr to B1

    class B2 {};
    typedef void (B2::*PB2) (); // memptr to B2

    class D1 : public B1, public B2 {};
    typedef void (D1::*PD) (); // memptr to D1

    struct S {
         operator PB1(); // can be converted to PD
    } s;
    struct T {
         operator PB2(); // can be converted to PD
    } t;

    void foo() {
         s == t; // Is this an error?
    }

According to 13.6 [over.built] paragraph 16, there is an operator== for PD (“For every pointer to member type...”), so why wouldn't it be used for this comparison?

Mike Miller: The problem, as I understand it, is that 13.3.1.2 [over.match.oper] paragraph 3, bullet 3, sub-bullet 3 is broader than it was intended to be. It says that candidate built-in operators must “accept operand types to which the given operand or operands can be converted according to 13.3.3.1 [over.best.ics].” 13.3.3.1.2 [over.ics.user] describes user-defined conversions as having a second standard conversion sequence, and there is nothing to restrict that second standard conversion sequence.

My initial thought on addressing this would be to say that user-defined conversion sequences whose second standard conversion sequence contains a pointer conversion or a pointer-to-member conversion are not considered when selecting built-in candidate operator functions. They would still be applicable after the hand-off to Clause 5 (e.g., in bringing the operands to their common type, 5.10 [expr.eq], or composite pointer type, 5.9 [expr.rel]), just not in constructing the list of built-in candidate operator functions.

I started to suggest restricting the second standard conversion sequence to conversions having Promotion or Exact Match rank, but that would exclude the Boolean conversions, which are needed for !, &&, and ||. (It would have also restricted the floating-integral conversions, though, which might be a good idea. They can't be used implicitly, I think, because there would be an ambiguity among all the promoted integral types; however, none of the compilers I tested even tried those conversions because the errors I got were not ambiguities but things like “floating point operands not allowed for %”.)

Bill Gibbons: I recall seeing this problem before, though possibly not in committee discussions. As written this rule makes the set of candidate functions dependent on what classes have been defined, including classes not otherwise required to have been defined in order for "==" to be meaningful. For templates this implies that the set is dependent on what templates have been instantiated, e.g.

  template<class T> class U : public T { };
  U<B1> u;  // changes the set of candidate functions to include
            // operator==(U<B1>,U<B1>)?

There may be other places where the existence of a class definition, or worse, a template instantiation, changes the semantics of an otherwise valid program (e.g. pointer conversions?) but it seems like something to be avoided.

(See also issue 954.)




1919. Overload resolution for ! with explicit conversion operator

Section: 13.3.1.2  [over.match.oper]     Status: open     Submitter: Johannes Schaub     Date: 2014-04-30

Although the intent is that the ! operator should be usable with an operand that is a class object having an explicit conversion to bool (i.e., its operand is “contextually converted to bool”), the selection of the conversion operator is done via 13.3.1.2 [over.match.oper], 13.3.2 [over.match.viable], and 13.3.3 [over.match.best], which do not make specific allowance for this special characteristic of the ! operator and thus will not select the explicit conversion function.

Notes from the June, 2014 meeting:

CWG noted that this same issue affects && and ||.




1781. Converting from nullptr_t to bool in overload resolution

Section: 13.3.1.5  [over.match.conv]     Status: open     Submitter: Hubert Tong     Date: 2013-09-26

According to 13.3.1.5 [over.match.conv] paragraph 1, when a class type S is used as an initializer for an object of type T,

The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1 [over.ics.scs]) are candidate functions.

Because conversion from std::nullptr_t to bool is only permitted in direct-initialization (4.13 [conv.fctptr]), it is not clear whether there is a standard conversion sequence from std::nullptr_t to bool, considering that an implicit conversion sequence is intended to model copy-initialization. Should 13.3.1.5 [over.match.conv] be understood to refer only to conversions permitted in copy-initialization, or should the form of the initialization be considered? For example,

  struct SomeType {
    operator std::nullptr_t();
  };
  bool b{ SomeType() };    // Well-formed?

Note also 13.3.3.2 [over.ics.rank] paragraph 4, which may bear on the intent (or, alternatively, might describe a situation that cannot arise):

A conversion that does not convert a pointer, a pointer to member, or std::nullptr_t to bool is better than one that does.

See also issues 2133 and 2243.)




418. Imperfect wording on error on multiple default arguments on a called function

Section: 13.3.3  [over.match.best]     Status: open     Submitter: Chris Bowler     Date: 27 May 2003

According to 13.3.3 [over.match.best] paragraph 4, the following program appears to be ill-formed:

  void f(int, int=0);
  void f(int=0, int);

  void g() {
    f();
  }

Though I do not expect this is the intent of this paragraph in the standard.

13.3.3 [over.match.best] paragraph 4:

If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these declarations or the declarations they refer to in the case of using-declarations specify a default argument that made the function viable, the program is ill-formed. [Example:
namespace A {
  extern "C" void f(int = 5);
}
namespace B {
  extern "C" void f(int = 5);
}
using A::f;
using B::f;
void use() {
f(3); //OK, default argument was not used for viability
f(); //Error: found default argument twice
}
end example]



1459. Reference-binding tiebreakers in overload resolution

Section: 13.3.3.2  [over.ics.rank]     Status: open     Submitter: Jason Merrill     Date: 2012-02-07

Both paragraph 3 and paragraph 4 of 13.3.3.2 [over.ics.rank] have overload resolution tiebreakers for reference binding. It might be possible to merge those into a single treatment.




2277. Ambiguity inheriting constructors with default arguments

Section: 13.3.3.2  [over.ics.rank]     Status: open     Submitter: Richard Smith     Date: 2016-06-23

[Detailed description pending.]




1038. Overload resolution of &x.static_func

Section: 13.4  [over.over]     Status: open     Submitter: Mike Miller     Date: 2010-03-02

The Standard is not clear whether the following example is well-formed or not:

    struct S {
        static void f(int);
        static void f(double);
    };
    S s;
    void (*pf)(int) = &s.f;

According to 5.2.5 [expr.ref] paragraph 4 bullet 3, you do function overload resolution to determine whether x.f is a static or non-static member function. 5.3.1 [expr.unary.op] paragraph 6 says that you can only take the address of an overloaded function in a context that determines the overload to be chosen, and the initialization of a function pointer is such a context (13.4 [over.over] paragraph 1). The problem is that 13.4 [over.over] is phrased in terms of “an overloaded function name,” and this is a member access expression, not a name.

There is variability among implementations as to whether this example is accepted; some accept it as written, some only if the & is omitted, and some reject it in both forms.

Additional note (October, 2010):

A related question concerns an example like

    struct S {
        static void g(int*) {}
        static void g(long) {}
    } s;

    void foo() {
        (&s.g)(0L);
    }

Because the address occurs in a call context and not in one of the contexts mentioned in 13.4 [over.over] paragraph 1, the call expression in foo is presumably ill-formed. Contrast this with the similar example

    void g1(int*) {}
    void g1(long) {}

    void foo1() {
        (&g1)(0L);
    }

This call presumably is well-formed because 13.3.1.1 [over.match.call] applies to “the address of a set of overloaded functions.” (This was clearer in the wording prior to the resolution of issue 704: “...in this context using &F behaves the same as using the name F by itself.”) It's not clear that there's any reason to treat these two cases differently.

This question also bears on the original question of this issue, since the original wording of 13.3.1.1 [over.match.call] also described the case of an ordinary member function call like s.g(0L) as involving the “name” of the function, even though the postfix-expression is a member access expression and not a “name.” Perhaps the reference to “name” in 13.4 [over.over] should be similarly understood as applying to member access expressions?




1549. Overloaded comma operator with void operand

Section: 13.5.2  [over.binary]     Status: open     Submitter: Nikolay Ivchenkov     Date: 2012-09-04

Even though a function cannot take a parameter of type void, the current rules for overload resolution require consideration of overloaded operators when one operand has a user-defined or enumeration type and the other has type void. This can result in side effects and possibly errors, for example:

  template <class T> struct A {
    T t;
    typedef T type;
  };

  struct X {
    typedef A<void> type;
  };

  template <class T> void operator ,(typename T::type::type, T) {}

  int main() {
    X(), void(); // OK
    void(), X(); // error: A<void> is instantiated with a field of
                 // type void
  }



1620. User-defined literals and extended integer types

Section: 13.5.8  [over.literal]     Status: open     Submitter: Jason Merrill     Date: 2013-02-12

Although numeric literals can have extended integer types, user-defined literal operators cannot have a parameter of an extended integer type. This seems like an oversight.




260. User-defined conversions and built-in operator=

Section: 13.6  [over.built]     Status: open     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.




954. Overload resolution of conversion operator templates with built-in types

Section: 13.6  [over.built]     Status: open     Submitter: Steve Clamage     Date: 19 August, 2009

Consider the following example:

    struct NullClass {
        template<typename T> operator T () { return 0 ; }
    };

    int main() {
        NullClass n;
        n==5;        // #1
        return 0;
    }

The comparison at #1 is, according to the current Standard, ambiguous. According to 13.6 [over.built] paragraph 12, the candidates for operator==(L, R) include functions “for every pair of promoted arithmetic types,” so L could be either int or long, and the conversion operator template will provide an exact match for either.

Some implementations unambiguously choose the int candidate. Perhaps the overload resolution rules could be tweaked to prefer candidates in which L and R are the same type?

(See also issue 545.)




110. Can template functions and classes be declared in the same scope?

Section: 14  [temp]     Status: open     Submitter: John Spicer     Date: 28 Apr 1999

According to 14 [temp] paragraph 5,

Except that a function template can be overloaded either by (non-template) functions with the same name or by other function templates with the same name (14.8.3 [temp.over] ), a template name declared in namespace scope or in class scope shall be unique in that scope.
3.3.10 [basic.scope.hiding] paragraph 2 agrees that only functions, not function templates, can hide a class name declared in the same scope:
A class name (9.1 [class.name] ) or enumeration name (7.2 [dcl.enum] ) can be hidden by the name of an object, function, or enumerator declared in the same scope.
However, 3.3 [basic.scope] paragraph 4 treats functions and template functions together in this regard:
Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,

John Spicer: You should be able to take an existing program and replace an existing function with a function template without breaking unrelated parts of the program. In addition, all of the compilers I tried allow this usage (EDG, Sun, egcs, Watcom, Microsoft, Borland). I would recommend that function templates be handled exactly like functions for purposes of name hiding.

Martin O'Riordan: I don't see any justification for extending the purview of what is decidedly a hack, just for the sake of consistency. In fact, I think we should go further and in the interest of consistency, we should deprecate the hack, scheduling its eventual removal from the C++ language standard.

The hack is there to allow old C programs and especially the 'stat.h' file to compile with minimum effort (also several other Posix and X headers). People changing such older programs have ample opportunity to "do it right". Indeed, if you are adding templates to an existing program, you should probably be placing your templates in a 'namespace', so the issue disappears anyway. The lookup rules should be able to provide the behaviour you need without further hacking.




2255. Instantiated static data member templates

Section: 14  [temp]     Status: open     Submitter: Mike Miller     Date: 2016-03-29

The current wording does not state that a specialization of a static data member template (14 [temp] paragraph 1) is a static data member, which leaves the status of an example like the following unclear (since 5.2.5 [expr.ref] bullet 4.1 is phrased in terms of static data members):

  template <class T> struct A {
    template <class U> static const U x = 1;
    static const int y = 2;
  };

  int main() {
    A<int> a;
  int y = a.y;         // OK
  int x = a.x<int>     // ???



579. What is a “nested” > or >>?

Section: 14.2  [temp.names]     Status: open     Submitter: Daveed Vandevoorde     Date: 11 May 2006

The Standard does not normatively define which > and >> tokens are to be taken as closing a template-argument-list; instead, 14.2 [temp.names] paragraph 3 uses the undefined and imprecise term “non-nested:”

When parsing a template-id, the first non-nested > is taken as the end of the template-argument-list rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id.

The (non-normative) footnote clarifies that

A > that encloses the type-id of a dynamic_cast, static_cast, reinterpret_cast or const_cast, or which encloses the template-arguments of a subsequent template-id, is considered nested for the purpose of this description.

Aside from the questionable wording of this footnote (e.g., in what sense does a single terminating character “enclose” anything, and is a nested template-id “subsequent?”) and the fact that it is non-normative, it does not provide a complete definition of what “nesting” is intended to mean. For example, is the first > in this putative template-id “nested” or not?

    X<a ? b > c : d>

Additional note (January, 2014):

A similar problem exists for an operator> template:

  struct S;
  template<void (*)(S, S)> struct X {};
  void operator>(S, S);
  X<operator> > x;

Somehow the specification must be written to avoid taking the > token in the operator name as the end of the template argument list for X.




440. Allow implicit pointer-to-member conversion on nontype template argument

Section: 14.3  [temp.arg]     Status: open     Submitter: David Abrahams     Date: 13 Nov 2003

None of my compilers accept this, which surprised me a little. Is the base-to-derived member function conversion considered to be a runtime-only thing?

  template <class D>
  struct B
  {
      template <class X> void f(X) {}
      template <class X, void (D::*)(X) = &B<D>::f<X> >
      struct row {};
  };
  struct D : B<D>
  {
      void g(int);
      row<int,&D::g> r1;
      row<char*> r2;
  };

John Spicer: This is not among the permitted conversions listed in 14.3.

I'm not sure there is a terribly good reason for that. Some of the template argument rules for external entities were made conservatively because of concerns about issues of mangling template argument names.

David Abrahams: I'd really like to see that restriction loosened. It is a serious inconvenience because there appears to be no way to supply a usable default in this case. Zero would be an OK default if I could use the function pointer's equality to zero as a compile-time switch to choose an empty function implementation:

  template <bool x> struct tag {};

  template <class D>
  struct B
  {
      template <class X> void f(X) {}

      template <class X, void (D::*pmf)(X) = 0 >
      struct row {
          void h() { h(tag<(pmf == 0)>(), pmf); }
          void h(tag<1>, ...) {}
          void h(tag<0>, void (D::*q)(X)) { /*something*/}
      };
  };

  struct D : B<D>
  {
      void g(int);
      row<int,&D::g> r1;
      row<char*> r2;
  };

But there appears to be no way to get that effect either. The result is that you end up doing something like:

      template <class X, void (D::*pmf)(X) = 0 >
      struct row {
          void h() { if (pmf) /*something*/ }
      };

which invariably makes compilers warn that you're switching on a constant expression.




2105. When do the arguments for a parameter pack end?

Section: 14.3  [temp.arg]     Status: open     Submitter: Hubert Tong     Date: 2015-03-17

There does not appear to be a clear statement in the Standard that the first template parameter pack in a template parameter list corresponds to all remaining arguments in the template argument list. For example:

  template <int> struct A;

  template <int ...N, typename T> void foo(A<N> *..., T);
  void bar() {
   foo<0>(0, 0);      // okay: N consists of one template parameter, 0. T is deduced to int
   foo<0, int>(0, 0); // error: int does not match the form of the corresponding parameter N
  }

See also issue 2055.

Notes from the February, 2016 meeting:

The comments in the example reflect the intent.




1918. friend templates with dependent scopes

Section: 14.5.4  [temp.friend]     Status: open     Submitter: Richard Smith     Date: 2014-04-27

It is not clear what should happen for an example like:

  template<typename T> struct A {
    class B {
      class C {};
    };
  };
  class X {
    static int x;
    template <typename T> friend class A<T>::B::C;
  };
  template<> struct A<int> {
    typedef struct Q B;
  };
  struct Q {
    class C {
      int f() { return X::x; }
    };
  };

It appears that the friend template matches Q::C, because that class is also A<int>::B::C, but neither GCC nor EDG allow this code (saying X::x is inaccessible). (Clang doesn't support friend template declarations with a dependent scope.)

A strict reading of 14.5.4 [temp.friend] paragraph 5 might suggest that the friend declaration itself is ill-formed, because it does not declare a member of a class template, but I can't find any compiler that implements template friends that way.




1945. Friend declarations naming members of class templates in non-templates

Section: 14.5.4  [temp.friend]     Status: open     Submitter: Richard Smith     Date: 2014-06-19

During the discussion of issue 1918, it was decided that the last part of the issue should be split off into a separate issue. According to 14.5.4 [temp.friend] paragraph 5,

A member of a class template may be declared to be a friend of a non-template class.

Does this make the example from issue 1918,

  template<typename T> struct A {
    class B {
      class C {};
    };
  };
  class X {
    static int x;
    template <typename T> friend class A<T>::B::C;
  };
  template<> struct A<int> {
    typedef struct Q B;
  };
  struct Q {
    class C {
      int f() { return X::x; }
    };
  };

ill-formed because the friend declaration does not refer to a member of a class template? This does not appear to be the interpretation chosen by most implementations.




2261. Explicit instantiation of in-class friend definition

Section: 14.5.4  [temp.friend]     Status: open     Submitter: Jens Maurer     Date: 2016-04-28

[Detailed description pending.]




708. Partial specialization of member templates of class templates

Section: 14.5.5  [temp.class.spec]     Status: open     Submitter: James Widman     Date: 8 Aug, 2008

The Standard does not appear to specify clearly the effect of a partial specialization of a member template of a class template. For example:

    template<class T> struct B {
         template<class U> struct A { // #1
             void h() {}
         };
         template<class U> struct A<U*> {  // #2
             void f() {}
         };
    };

    template<> template<class U> struct B<int>::A { // #3
         void g() {}
    };

    void q(B<int>::A<char*>& p) {
         p.f();  // #4
    }

The explicit specialization at #3 replaces the primary member template #1 of B<int>; however, it is not clear whether the partial specialization #2 should be considered to apply to the explicitly-specialized member template of A<int> (thus allowing the call to p.f() at #4) or whether the partial specialization will be used only for specializations of B that are implicitly instantiated (meaning that #4 could call p.g() but not p.f()).




2173. Partial specialization with non-deduced contexts

Section: 14.5.5  [temp.class.spec]     Status: open     Submitter: Mike Miller     Date: 2015-09-14

During the discussion of issue 1315, it was observed that the example

  template <int I, int J> struct B {};
  template <int I> struct B<I, I*2> {};

is ill-formed because the deduction succeeds in both directions. This seems surprising. It was suggested that perhaps a non-deduced context should be considered more specialized than a deduced context.




310. Can function templates differing only in parameter cv-qualifiers be overloaded?

Section: 14.5.6.1  [temp.over.link]     Status: open     Submitter: Andrei Iltchenko     Date: 29 Aug 2001

I get the following error diagnostic [from the EDG front end]:

line 8: error: function template "example<T>::foo<R,A>(A)" has
          already been declared
     R  foo(const A);
        ^
when compiling this piece of code:
struct  example  {
   template<class R, class A>   // 1-st member template
   R  foo(A);
   template<class R, class A>   // 2-nd member template
   const R  foo(A&);
   template<class R, class A>   // 3-d  member template
   R  foo(const A);
};

/*template<> template<>
int  example<char>::foo(int&);*/


int  main()
{
   int  (example<char>::* pf)(int&) =
      &example<char>::foo;
}

The implementation complains that

   template<class R, class A>   // 1-st member template
   R  foo(A);
   template<class R, class A>   // 3-d  member template
   R  foo(const A);
cannot be overloaded and I don't see any reason for it since it is function template specializations that are treated like ordinary non-template functions, meaning that the transformation of a parameter-declaration-clause into the corresponding parameter-type-list is applied to specializations (when determining its type) and not to function templates.

What makes me think so is the contents of 14.5.6.1 [temp.over.link] and the following sentence from 14.8.2.1 [temp.deduct.call] "If P is a cv-qualified type, the top level cv-qualifiers of P are ignored for type deduction". If the transformation was to be applied to function templates, then there would be no reason for having that sentence in 14.8.2.1 [temp.deduct.call].

14.8.2.2 [temp.deduct.funcaddr], which my example is based upon, says nothing about ignoring the top level cv-qualifiers of the function parameters of the function template whose address is being taken.

As a result, I expect that template argument deduction will fail for the 2-nd and 3-d member templates and the 1-st one will be used for the instantiation of the specialization.




1644. Equivalent exception-specifications in function template declarations

Section: 14.5.6.1  [temp.over.link]     Status: open     Submitter: Richard Smith     Date: 2013-03-17

Although 15.4 [except.spec] paragraph 3 says,

Two exception-specifications are compatible if:

it is not clear whether “equivalent” in this context should be taken as a reference to the definition of equivalent given in 14.5.6.1 [temp.over.link] paragraph 5:

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule (3.2 [basic.def.odr]), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression.

since the context there is expressions that appear in function template parameters and return types.

There is implementation variance on this question.




402. More on partial ordering of function templates

Section: 14.5.6.2  [temp.func.order]     Status: open     Submitter: Nathan Sidwell     Date: 7 Apr 2003

This was split off from issue 214 at the April 2003 meeting.

Nathan Sidwell: John Spicer's proposed resolution does not make the following well-formed.

  template <typename T> int Foo (T const *) {return 1;} //#1
  template <unsigned I> int Foo (char const (&)[I]) {return 2;} //#2

  int main ()
  {
    return Foo ("a") != 2;
  }

Both #1 and #2 can deduce the "a" argument, #1 deduces T as char and #2 deduces I as 2. However, neither is more specialized because the proposed rules do not have any array to pointer decay.

#1 is only deduceable because of the rules in 14.8.2.1 [temp.deduct.call] paragraph 2 that decay array and function type arguments when the template parameter is not a reference. Given that such behaviour happens in deduction, I believe there should be equivalent behaviour during partial ordering. #2 should be resolved as more specialized as #1. The following alteration to the proposed resolution of DR214 will do that.

Insert before,

the following

For the example above, this change results in deducing 'T const *' against 'char const *' in one direction (which succeeds), and 'char [I]' against 'T const *' in the other (which fails).

John Spicer: I don't consider this a shortcoming of my proposed wording, as I don't think this is part of the current rules. In other words, the resolution of 214 might make it clearer how this case is handled (i.e., clearer that it is not allowed), but I don't believe it represents a change in the language.

I'm not necessarily opposed to such a change, but I think it should be reviewed by the core group as a related change and not a defect in the proposed resolution to 214.

Notes from the October 2003 meeting:

There was some sentiment that it would be desirable to have this case ordered, but we don't think it's worth spending the time to work on it now. If we look at some larger partial ordering changes at some point, we will consider this again.




1157. Partial ordering of function templates is still underspecified

Section: 14.5.6.2  [temp.func.order]     Status: open     Submitter: CA     Date: 2010-08-03

N3092 comment CA 7

14.5.6.2 [temp.func.order] paragraph 3 says,

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3 [temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

The characteristics of the synthesized entities and how they are determined is not specified. For example, members of a dependent type referred to in non-deduced contexts are not specified to exist, even though the transformed function type would be invalid in their absence.

Example 1:

  template<typename T, typename U> struct A;
  template<typename T> void foo(A<T, typename T::u> *) { } // #1
    // synthetic T1 has member T1::u
  template <typename T> void foo(A<T, typename T::u::v> *) { } // #2
    // synthetic T2 has member T2::u and member T2::u::v
    // T in #1 deduces to synthetic T2 in partial ordering;
    // deduced A for the parameter is A<T2, T2::u> * --this is not necessarily compatible
    // with A<T2, T2::u::v> * and it does not need to be. See Note 1. The effect is that
    // (in the call below) the compatibility of B::u and B::u::v is respected.
    // T in #2 cannot be successfully deduced in partial ordering from A<T1, T1::u> *;
    // invalid type T1::u::v will be formed when T1 is substituted into non-deduced contexts.
  struct B {
    struct u { typedef u v; };
  };
  int main() {
    foo((A<B, B::u> *)0); // calls #2
  }

Note 1: Template argument deduction is an attempt to match a P and a deduced A; however, template argument deduction is not specified to fail if the P and the deduced A are incompatible. This may occur in the presence of non-deduced contexts. Notwithstanding the parenthetical statement in 14.8.2.4 [temp.deduct.partial] paragraph 9, template argument deduction may succeed in determining a template argument for every template parameter while producing a deduced A that is not compatible with the corresponding P.

Example 2:

  template <typename T, typename U, typename V> struct A;
  template <typename T>
    void foo(A<T, struct T::u, struct T::u::u> *); // #2.1
      // synthetic T1 has member non-union class T1::u
  template <typename T, typename U>
    void foo(A<T, U , U> *); // #2.2
      // synthetic T2 and U2 has no required properties
      // T in #2.1 cannot be deduced in partial ordering from A<T2, U2, U2> *;
      // invalid types T2::u and T2::u::u will be formed when T2 is substituted in nondeduced contexts.
      // T and U in #2.2 deduces to, respectively, T1 and T1::u from A<T1, T1::u, struct
T1::u::u> * unless
      // struct T1::u::u does not refer to the injected-class-name of the class T1::u (if that is possible).
  struct B {
    struct u { };
  };
  int main() {
    foo((A<B, B::u, struct B::u::u> *)0); // calls #2.1
  }

It is, however, unclear to what extent an implementation will have to go to determine these minimal properties.




2160. Issues with partial ordering

Section: 14.5.6.2  [temp.func.order]     Status: open     Submitter: Richard Smith     Date: 2015-07-16

(From this editorial issue.)

Consistency of deduced values

  template <typename T> void foo(T, T); // (1)
  template <typename T, typename U> void foo(T, U); // (2)

14.8.2.5 [temp.deduct.type] paragraph 2 makes it clear that there must be exactly one set of deduced values for the Ps. But there is no such statement in the partial ordering rule. The algorithm described only does pairwise P/A matching, so a synthesized call from (2) to (1) via foo(U{}, V{}) could succeed in deduction. Both gcc and clang agree that (1) is more specialized.

Type Synthesis Template Instantiation

  template <typename T>
  struct identity { using type = T; };

  template<typename T> void bar(T, T ); // (1) 
  template<typename T> void bar(T, typename identity<T>::type ); // (2)

Here, if synthesized for (2) Unique2 and typename identity<Unique2>::type == Unique2 , then type deduction would succeed in both directions and the call bar(0,0) would be ambiguous. However, it seems that both compilers instead simply treat typename identity<Unique2>::type as Unique2_b, thus making template deduction from (2) to (1) fail (based on the implied missing Consistency rule).

Non-deduced Context Omission

This is the same as the previous example, except now define

  template <typename T> struct identity;
  template <> struct identity<int> { using type = int; };

With no template instantiation during synthesis and consistency, the (2) ==> (1) deduction fails. But if we consider the (1) ==> (2) call, we'd match T against Unique1 and then have the non-deduced context typename identity<Unique1>::type to match against Unique1, but that would be a substitution failure. It seems that the approach taken by gcc and clang (both of which prefer (1) here) is to ignore the non-deduced context argument, as long as that parameter type is deduced from a different template parameter type that did get matched.

Notes from the February, 2016 meeting:

None of these examples appears to reflect a defect in the current wording; in particular, the second and third examples involve a dependent type and there could be a later specialization of identity, so it's impossible to reason about those cases in the template definition context. The issue will be left open to allow for possible clarification of the intent of the wording.




2236. When is an alias template specialization dependent?

Section: 14.5.7  [temp.alias]     Status: open     Submitter: Maxim Kartashev     Date: 2016-03-01

[Detailed description pending.]




1257. Instantiation via non-dependent references in uninstantiated templates

Section: 14.6  [temp.res]     Status: open     Submitter: Johannes Schaub     Date: 2011-03-09

The Standard does not appear to specify whether a non-dependent reference to a template specialization in a template definition that is never instantiated causes the implicit instantiation of the referenced specialization.




1974. Redundant specification of non-type typename-specifier

Section: 14.6  [temp.res]     Status: open     Submitter: Hubert Tong     Date: 2014-07-17

Paragraphs 3-4 of 14.6 [temp.res] read, in part,

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1 [temp.dep.type]) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

If a specialization of a template is instantiated for a set of template-arguments such that the qualified-id prefixed by typename does not denote a type, the specialization is ill-formed.

The former requirement is intended to apply to the definition and the latter to an instantiation of a template, but that intent is not completely clear, leading to the perception that they are redundant.




2067. Generated variadic templates requiring empty pack

Section: 14.6  [temp.res]     Status: open     Submitter: Richard Smith     Date: 2015-01-09

According to 14.6 [temp.res] paragraph 8,

If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

I'm inclined to think that this rule should only apply to code the user wrote. That is, if every valid instantiation of an entity (that was not itself instantiated) requires at least one of the enclosing template argument lists to include an empty template argument pack, then the program is ill-formed (no diagnostic required).




186. Name hiding and template template-parameters

Section: 14.6.1  [temp.local]     Status: open     Submitter: John Spicer     Date: 11 Nov 1999

The standard prohibits a class template from having the same name as one of its template parameters (14.6.1 [temp.local] paragraph 4). This prohibits

    template <class X> class X;
for the reason that the template name would hide the parameter, and such hiding is in general prohibited.

Presumably, we should also prohibit

    template <template <class T> class T> struct A;
for the same reason.


459. Hiding of template parameters by base class members

Section: 14.6.1  [temp.local]     Status: open     Submitter: Daveed Vandevoorde     Date: 2 Feb 2004

Currently, member of nondependent base classes hide references to template parameters in the definition of a derived class template.

Consider the following example:

   class B {
      typedef void *It;    // (1)
      // ...
    };

    class M: B {};

    template<typename> X {};

    template<typename It> struct S   // (2)
        : M, X<It> {   // (3)
      S(It, It);   // (4)
      // ...
    };

As the C++ language currently stands, the name "It" in line (3) refers to the template parameter declared in line (2), but the name "It" in line (4) refers to the typedef in the private base class (declared in line (1)).

This situation is both unintuitive and a hindrance to sound software engineering. (See also the Usenet discussion at http://tinyurl.com/32q8d .) Among other things, it implies that the private section of a base class may change the meaning of the derived class, and (unlike other cases where such things happen) there is no way for the writer of the derived class to defend the code against such intrusion (e.g., by using a qualified name).

Changing this can break code that is valid today. However, such code would have to:

  1. name a template parameter and not use it after the opening brace, and
  2. use that same name to access a base-class name within the braces.
I personally have no qualms breaking such a program.

It has been suggested to make situations like these ill-formed. That solution is unattractive however because it still leaves the writer of a derived class template without defense against accidental name conflicts with base members. (Although at least the problem would be guaranteed to be caught at compile time.) Instead, since just about everyone's intuition agrees, I would like to see the rules changed to make class template parameters hide members of the same name in a base class.

See also issue 458.

Notes from the March 2004 meeting:

We have some sympathy for a change, but the current rules fall straightforwardly out of the lookup rules, so they're not “wrong.” Making private members invisible also would solve this problem. We'd be willing to look at a paper proposing that.

Additional discussion (April, 2005):

John Spicer: Base class members are more-or-less treated as members of the class, [so] it is only natural that the base [member] would hide the template parameter.

Daveed Vandevoorde: Are base class members really “more or less” members of the class from a lookup perspective? After all, derived class members can hide base class members of the same name. So there is some pretty definite boundary between those two sets of names. IMO, the template parameters should either sit between those two sets, or they should (for lookup purposes) be treated as members of the class they parameterize (I cannot think of a practical difference between those two formulations).

John Spicer: How is [hiding template parameters] different from the fact that namespace members can be hidden by private parts of a base class? The addition of int C to N::A breaks the code in namespace M in this example:

    namespace N {
       class A {
    private:
         int C;
       };
    }

    namespace M {
       typedef int C;
       class B : public N::A {
         void f() {
             C c;
         }
       };
    }

Daveed Vandevoorde: C++ has a mechanism in place to handle such situations: qualified names. There is no such mechanism in place for template parameters.

Nathan Myers: What I see as obviously incorrect ... is simply that a name defined right where I can see it, and directly attached to the textual scope of B's class body, is ignored in favor of something found in some other file. I don't care that C1 is defined in A, I have a C1 right here that I have chosen to use. If I want A::C1, I can say so.

I doubt you'll find any regular C++ coder who doesn't find the standard behavior bizarre. If the meaning of any code is changed by fixing this behavior, the overwhelming majority of cases will be mysterious bugs magically fixed.

John Spicer: I have not heard complaints that this is actually a cause of problems in real user code. Where is the evidence that the status quo is actually causing problems?

In this example, the T2 that is found is the one from the base class. I would argue that this is natural because base class members are found as part of the lookup in class B:

    struct A {
             typedef int T2;
    };
    template <class T2> struct B : public A {
             typedef int T1;
             T1 t1;
             T2 t2;
    };

This rule that base class members hide template parameters was formalized about a dozen years ago because it fell out of the principle that base class members should be found at the same stage of lookup as derived class members, and that to do otherwise would be surprising.

Gabriel Dos Reis: The bottom line is that:

  1. the proposed change is a silent change of meaning;
  2. the proposed change does not make the language any more regular; the current behavior is consistent with everything else, however “surprising” that might be;
  3. the proposed change does have its own downsides.

Unless presented with real major programming problems the current rules exhibit, I do not think the simple rule “scopes nest” needs a change that silently mutates program meaning.

Mike Miller: The rationale for the current specification is really very simple:

  1. “Unless redeclared in the derived class, members of a base class are also considered to be members of the derived class.” (10 [class.derived] paragraph 2)
  2. In class scope, members hide nonmembers.

That's it. Because template parameters are not members, they are hidden by member names (whether inherited or not). I don't find that “bizarre,” or even particularly surprising.

I believe these rules are straightforward and consistent, so I would be opposed to changing them. However, I am not unsympathetic toward Daveed's concern about name hijacking from base classes. How about a rule that would make a program ill-formed if a direct or inherited member hides a template parameter?

Unless this problem is a lot more prevalent than I've heard so far, I would not want to change the lookup rules; making this kind of collision a diagnosable error, however, would prevent hijacking without changing the lookup rules.

Erwin Unruh: I have a different approach that is consistent and changes the interpretation of the questionable code. At present lookup is done in this sequence:

If we change this order to

it is still consistent in that no lookup is placed between the base class and the derived class. However, it introduces another inconsistency: now scopes do not nest the same way as curly braces nest — but base classes are already inconsistent this way.

Nathan Myers: This looks entirely satisfactory. If even this seems like too big a change, it would suffice to say that finding a different name by this search order makes the program ill-formed. Of course, a compiler might issue only a portability warning in that case and use the name found Erwin's way, anyhow.

Gabriel Dos Reis: It is a simple fact, even without templates, that a writer of a derived class cannot protect himself against declaration changes in the base class.

Richard Corden: If a change is to be made, then making it ill-formed is better than just changing the lookup rules.

    struct B
    {
      typedef int T;
      virtual void bar (T const & );
    };

    template <typename T>
    struct D : public B
    {
      virtual void bar (T const & );
    };

    template class D<float>;

I think changing the semantics of the above code silently would result in very difficult-to-find problems.

Mike Miller: Another case that may need to be considered in deciding on Erwin's suggestion or the “ill-formed” alternative is the treatment of friend declarations described in 3.4.1 [basic.lookup.unqual] paragraph 10:

    struct A {
        typedef int T;
        void f(T);
    };
    template<typename T> struct B {
        friend void A::f(T);  // Currently T is A::T
    };

Notes from the October, 2005 meeting:

The CWG decided not to consider a change to the existing rules at this time without a paper exploring the issue in more detail.




1619. Definition of current instantiation

Section: 14.6.2.1  [temp.dep.type]     Status: open     Submitter: Johannes Schaub     Date: 2013-02-04

The definition of the current instantiation, given in 14.6.2.1 [temp.dep.type] paragraph 1, is phrased in terms of the meaning of a name (“A name refers to the current instantiation if it is...”); it does not define when a type is the current instantiation. Thus the interpretation of *this and of phrases like “member of a class that is the current instantiation” is not formally specified.




1829. Dependent unnamed types

Section: 14.6.2.1  [temp.dep.type]     Status: open     Submitter: Hubert Tong     Date: 2014-01-08

The specification of dependent types in 14.6.2.1 [temp.dep.type] is given in terms of names. However, one might consider some unnamed types as dependent. Consider the following example:

  template <typename T> struct A {
    struct { } obj;
    void foo() {
      bar(obj); // lookup for bar when/where?
    }
  };

  void bar(...);

  int main() {
    A<int> a;
    a.foo();    // calls bar(...)?
  }

If the type of A::obj had a name, it would be dependent. However, the rationale for making nested types dependent is that they are subject to explicit specialization and thus not knowable at the point of the template definition. An unnamed type, as in this example, cannot be explicitly specialized and thus could be considered as a member of the current instantiation. Which treatment is intended?

Notes from the February, 2014 meeting:

There are other cases in which a named entity is dependnet, even though it cannot be explicitly specialized. CWG felt that the most consistent rule would be to make all nested classes dependent, whether named or not.




2266. Has dependent type vs is type-dependent

Section: 14.6.2.1  [temp.dep.type]     Status: open     Submitter: Fedor Sergeev     Date: 2016-05-20

[Detailed description pending.]




2275. Type-dependence of function template

Section: 14.6.2.2  [temp.dep.expr]     Status: open     Submitter: Jason Merrill     Date: 2016-06-21

[Detailed description pending.]




2276. Dependent noexcept and function type-dependence

Section: 14.6.2.3  [temp.dep.constexpr]     Status: open     Submitter: Maxim Kartashev     Date: 2016-06-23

[Detailed description pending.]




1028. Dependent names in non-defining declarations

Section: 14.6.4  [temp.dep.res]     Status: open     Submitter: Sean Hunt     Date: 2010-02-03

The current wording of 14.6.4 [temp.dep.res] seems to assume that dependent names can only appear in the definition of a template:

In resolving dependent names, names from the following sources are considered:

However, dependent names can occur in non-defining declarations of the template as well; for instance,

    template<typename T>
    T foo(T, decltype(bar(T())));

bar needs to be looked up, even though there is no definition of foo in the translation unit.

Additional note (February, 2011):

The resolution of this issue can't simply replace the word “definition” with the word “declaration,” mutatis mutandis, because there can be multiple declarations in a translation unit (which isn't true of “the definition”). As a result, the issue was moved back to "open" status for further consideration.




2245. Point of instantiation of incomplete class template

Section: 14.6.4.1  [temp.point]     Status: open     Submitter: Richard Smith     Date: 2016-03-08

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The consensus was that references to specializations before the template definition is seen are not points of instantiation.




2250. Implicit instantiation, destruction, and TUs

Section: 14.6.4.1  [temp.point]     Status: open     Submitter: Dawn Perchik     Date: 2016-03-21

[Detailed description pending.]

Notes from the December, 2016 teleconference:

The problem is that the current wording only connects name lookup with point of instantiation; other semantic checks, such as the requirement for completeness of a class, should also be performed at that point.




1500. Name lookup of dependent conversion function

Section: 14.6.4.2  [temp.dep.candidate]     Status: open     Submitter: Johannes Schaub     Date: 2012-04-27

Consider the following example:

  template<typename T>
  struct A {
   operator int() { return 0; }

   void f() {
    operator T();
   }
  };

  int main() {
   A<int> a;
   a.f();
  }

One might expect this to call operator int when instantiating. But since operator T is a dependent name, it is looked up by unqualified lookup only in the definition context, where it will find no declaration. Argument-dependent lookup will not find anything in the instantiation context either, so this code is ill-formed. If we change operator int() to operator T(), which is a seemingly unrelated change, the code becomes well-formed.

There is implementation variability on this point.




2118. Stateful metaprogramming via friend injection

Section: 14.6.5  [temp.inject]     Status: open     Submitter: Richard Smith     Date: 2015-04-27

Defining a friend function in a template, then referencing that function later provides a means of capturing and retrieving metaprogramming state. This technique is arcane and should be made ill-formed.

Notes from the May, 2015 meeting:

CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.




1378. When is an instantiation required?

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Jason Merrill     Date: 2011-08-18

A template instantiation can be “required” without there being a need for it at link time if it can appear in a constant expression:

    template <class T> struct A {
       static const T t;
    };
    template <class T> const T A<T>::t = 0;
    template <int I> struct B { };
    int a = sizeof(B<A<int>::t>);

    template <class T> constexpr T f(T t) { return t; }
    int b = sizeof(B<f(42)>);

It seems like it might be useful to define a term other than odr-used for this sort of use, which is like odr-used but doesn't depend on potentially evaluated context or lvalue-rvalue conversions.

Nikolay Ivchenkov:

Another possibility would be to introduce the extension described in the closed issue 1272 and then change 3.2 [basic.def.odr] paragraph 2 as follows:

An expression E is potentially evaluated unless it is an unevaluated operand (Clause 5 [expr]) or a subexpression thereof. if and only if

An expression S is a direct subexpression of an expression E if and only if S and E are different expressions, S is a subexpression of E, and there is no expression X such that X differs from both S and E, S is a subexpression of X, and X is a subexpression of E. A variable 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.20 [expr.const]) and the lvalue-to-rvalue conversion (4.1) is immediately applied...

[Example:

    template <class T> struct X {
        static int const m = 1;
        static int const n;
    };
    template <class T> int const X<T>::n = 2;

    int main() {
        // X<void>::m is odr-used,
        // X<void>::m is defined implicitly
        std::cout << X<void>::m << std::endl;

        // X<void>::n is odr-used,
        // X<void>::n is defined explicitly
        std::cout << X<void>::n << std::endl;

        // OK (issue 712 is not relevant here)
        std::cout << (1 ? X<void>::m : X<void>::n) << std::endl;
    }

(See also issues 712 and 1254.)




1602. Linkage of specialization vs linkage of template arguments

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Richard Smith     Date: 2013-01-09

The Standard does not appear to specify the linkage of a template specialization. 14.7.1 [temp.inst] paragraph 11 does say,

Implicitly instantiated class and function template specializations are placed in the namespace where the template is defined.

which could be read as implying that the specialization has the same linkage as the template itself. Implementation practice seems to be that the weakst linkage of the template and the arguments is used for the specialization.




1856. Indirect nested classes of class templates

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Richard Smith     Date: 2014-02-11

During the discussion of issue 1484, it was observed that the current rules do not adequately address indirect nested classes of class templates (i.e., member classes of member classes of class templates) in regard to their potential separate instantiation.




2202. When does default argument instantiation occur?

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Richard Smith     Date: 2015-11-19

[Detailed description pending.]




2222. Additional contexts where instantiation is not required

Section: 14.7.1  [temp.inst]     Status: open     Submitter: CWG     Date: 2016-01-11

[Detailed description pending.]




2263. Default argument instantiation for friends

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Hubert Tong     Date: 2016-05-04

[Detailed description pending.]

Notes from the December, 2016 teleconference:

This issue should be resolved by the resolution of issue 2174.




2265. Delayed pack expansion and member redeclarations

Section: 14.7.1  [temp.inst]     Status: open     Submitter: Hubert Tong     Date: 2016-05-11

[Detailed description pending.]




293. Syntax of explicit instantiation/specialization too permissive

Section: 14.7.2  [temp.explicit]     Status: open     Submitter: Mark Mitchell     Date: 27 Jun 2001

14.7.2 [temp.explicit] defines an explicit instantiation as

Syntactically, that allows things like:

    template int S<int>::i = 5, S<int>::j = 7;

which isn't what anyone actually expects. As far as I can tell, nothing in the standard explicitly forbids this, as written. Syntactically, this also allows:

    template namespace N { void f(); }

although perhaps the surrounding context is enough to suggest that this is invalid.

Suggested resolution:

I think we should say:

[Steve Adamczyk: presumably, this should have template at the beginning.]

and then say that:

There are similar problems in 14.7.3 [temp.expl.spec]:

Here, I think we want:

with similar restrictions as above.

[Steve Adamczyk: This also needs to have template <> at the beginning, possibly repeated.]




1046. What is a “use” of a class specialization?

Section: 14.7.2  [temp.explicit]     Status: open     Submitter: Michael Wong     Date: 2010-03-08

According to 14.7.2 [temp.explicit] paragraph 10,

An entity that is the subject of an explicit instantiation declaration and that is also used in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.

The term “used” is too vague and needs to be defined. In particular, “use” of a class template specialization as an incomplete type — to form a pointer, for instance — should not require the presence of an explicit instantiation definition elsewhere in the program.




2270. Non-inline functions and explicit instantiation declarations

Section: 14.7.2  [temp.explicit]     Status: open     Submitter: Richard Smith     Date: 2016-06-10

[Detailed description pending.]




2260. Explicit specializations of deleted member functions

Section: 14.7.3  [temp.expl.spec]     Status: open     Submitter: Richard Smith     Date: 2016-04-17

[Detailed description pending.]

Notes from the December, 2016 teleconference:

=delete definitions of member functions should be instantiated when instantiating a class template. That would make the example an ill-formed redefinition.




264. Unusable template constructors and conversion functions

Section: 14.8.1  [temp.arg.explicit]     Status: open     Submitter: John Spicer     Date: 17 Nov 2000

The note in paragraph 5 of 14.8.1 [temp.arg.explicit] makes clear that explicit template arguments cannot be supplied in invocations of constructors and conversion functions because they are called without using a name. However, there is nothing in the current wording of the Standard that makes declaring a constructor or conversion operator that is unusable because of nondeduced parameters (i.e., that would need to be specified explicitly) ill-formed. It would be a service to the programmer to diagnose this useless construct as early as possible.




2200. Conversions in template argument deduction

Section: 14.8.1  [temp.arg.explicit]     Status: open     Submitter: Vinny Romano     Date: 2015-11-14

[Detailed description pending.]




271. Explicit instantiation and template argument deduction

Section: 14.8.2  [temp.deduct]     Status: open     Submitter: John Spicer     Date: 20 Feb 2001

Nicolai Josuttis sent me an example like the following:

    template <typename RET, typename T1, typename T2>
    const RET& min (const T1& a, const T2& b)
    {
	return (a < b ? a : b);
    }
    template const int& min<int>(const int&,const int&);  // #1
    template const int& min(const int&,const int&);       // #2

Among the questions was whether explicit instantiation #2 is valid, where deduction is required to determine the type of RET.

The first thing I realized when researching this is that the standard does not really spell out the rules for deduction in declarative contexts (friend declarations, explicit specializations, and explicit instantiations). For explicit instantiations, 14.7.2 [temp.explicit] paragraph 2 does mention deduction, but it doesn't say which set of deduction rules from 14.8.2 [temp.deduct] should be applied.

Second, Nicolai pointed out that 14.7.2 [temp.explicit] paragraph 6 says

A trailing template-argument can be left unspecified in an explicit instantiation provided it can be deduced from the type of a function parameter (14.8.2 [temp.deduct]).

This prohibits cases like #2, but I believe this was not considered in the wording as there is no reason not to include the return type in the deduction process.

I think there may have been some confusion because the return type is excluded when doing deduction on a function call. But there are contexts where the return type is included in deduction, for example, when taking the address of a function template specialization.

Suggested resolution:

  1. Update 14.8.2 [temp.deduct] to include a section "Deducing template arguments from a declaration" that describes how deduction is done when finding a template that matches a declaration. This should, I believe, include the return type.
  2. Update 14.7.2 [temp.explicit] to make reference to the new rules in 14.8.2 [temp.deduct] and remove the description of the deduction details from 14.7.2 [temp.explicit] paragraph 6.



297. Which template does an explicit specialization specialize?

Section: 14.8.2  [temp.deduct]     Status: open     Submitter: Andrei Iltchenko     Date: 7 Jul 2001

Andrei Iltchenko points out that the standard has no wording that defines how to determine which template is specialized by an explicit specialization of a function template. He suggests "template argument deduction in such cases proceeds in the same way as when taking the address of a function template, which is described in 14.8.2.2 [temp.deduct.funcaddr]."

John Spicer points out that the same problem exists for all similar declarations, i.e., friend declarations and explicit instantiation directives. Finding a corresponding placement operator delete may have a similar problem.

John Spicer: There are two aspects of "determining which template" is referred to by a declaration: determining the function template associated with the named specialization, and determining the values of the template arguments of the specialization.

    template <class T> void f(T);  #1
    template <class T> void f(T*); #2
    template <> void f(int*);

In other words, which f is being specialized (#1 or #2)? And then, what are the deduced template arguments?

14.5.6.2 [temp.func.order] does say that partial ordering is done in contexts such as this. Is this sufficient, or do we need to say more about the selection of the function template to be selected?

14.8.2 [temp.deduct] probably needs a new section to cover argument deduction for cases like this.




697. Deduction rules apply to more than functions

Section: 14.8.2  [temp.deduct]     Status: open     Submitter: Doug Gregor     Date: 6 June, 2008

14.8.2 [temp.deduct] is all about function types, but these rules also apply, e.g., when matching a class template partial specialization. We should add a note stating that we could be doing substitution into the template-id for a class template partial specialization.

Additional note (August 2008):

According to 14.5.5.1 [temp.class.spec.match] paragraph 2, argument deduction is used to determine whether a given partial specialization matches a given argument list. However, there is nothing in 14.5.5.1 [temp.class.spec.match] nor in 14.8.2 [temp.deduct] and its subsections that describes exactly how argument deduction is to be performed in this case. It would seem that more than just a note is required to clarify this processing.




2054. Missing description of class SFINAE

Section: 14.8.2  [temp.deduct]     Status: open     Submitter: Ville Voutilainen     Date: 2014-12-07

Presumably something like the following should be well-formed, where a deduction failure in a partial specialization is handled as a SFINAE case as it is with function templates and not a hard error:

  template <class T, class U> struct X   {
    typedef char member;
  };

  template<class T> struct X<T,
   typename enable_if<(sizeof(T)>sizeof(
     float)), float>::type>
  {
    typedef long long member;
  };

  int main() {
    cout << sizeof(X<double, float>::member);
  }

However, this does not appear to be described anywhere in the Standard.




503. Cv-qualified function types in template argument deduction

Section: 14.8.2.1  [temp.deduct.call]     Status: open     Submitter: Gabriel Dos Reis     Date: 22 Feb 2005

Consider the following program:

    template <typename T> int ref (T&)                { return 0; }
    template <typename T> int ref (const T&)          { return 1; }
    template <typename T> int ref (const volatile T&) { return 2; }
    template <typename T> int ref (volatile T&)       { return 4; }

    template <typename T> int ptr (T*)                { return 0; }
    template <typename T> int ptr (const T*)          { return 8; }
    template <typename T> int ptr (const volatile T*) { return 16; }
    template <typename T> int ptr (volatile T*)       { return 32; }

    void foo() {}

    int main()
    {
        return ref(foo) + ptr(&foo);
    }

The Standard appears to specify that the value returned from main is 2. The reason for this result is that references and pointers are handled differently in template argument deduction.

For the reference case, 14.8.2.1 [temp.deduct.call] paragraph 3 says that “If P is a reference type, the type referred to by P is used for type deduction.” Because of issue 295, all four of the types for the ref function parameters are the same, with no cv-qualification; overload resolution does not find a best match among the parameters and thus the most-specialized function is selected.

For the pointer type, argument deduction does not get as far as forming a cv-qualified function type; instead, argument deduction fails in the cv-qualified cases because of the cv-qualification mismatch, and only the cv-unqualified version of ptr survives as a viable function.

I think the choice of ignoring cv-qualifiers in the reference case but not the pointer case is very troublesome. The reason is that when one considers function objects as function parameters, it introduces a semantic difference whether the function parameter is declared a reference or a pointer. In all other contexts, it does not matter: a function name decays to a pointer and the resulting semantics are the same.

(See also issue 1584.)




1221. Partial ordering and reference collapsing

Section: 14.8.2.4  [temp.deduct.partial]     Status: open     Submitter: Michael Wong     Date: 2010-11-08

The current partial ordering rules produce surprising results in the presence of reference collapsing.

Since partial ordering is currently based solely on the signature of the function templates, the lack of difference following substitution of the template type parameter in the following is not taken into account.

Especially unsettling is that the allegedly "more specialized" template (#2) is not a candidate in the first call where template argument deduction fails for it despite a lack of non-deduced contexts.

    template <typename T>
    void foo(T&&);  // #1

    template <typename T>
    void foo(volatile T&&);  // #2

    int main(void) {
      const int x = 0;
      foo(x);  // calls #1 with T='const int &'
      foo<const int &>(x);  // calls #2
    }



1763. Length mismatch in template type deduction

Section: 14.8.2.5  [temp.deduct.type]     Status: open     Submitter: Canada     Date: 2013-09-23

N3690 comment CA 4

It is not clear how an example like the following is to be handled:

  template <typename U> 
  struct A { 
    template <typename V> operator A<V>(); 
  }; 

  template <typename T> 
  void foo(A<void (T)>); 

  void foo(); 

  int main() { 
    A<void (int, char)> a; 
    foo<int>(a); 
    foo(a); // deduces T to be int
  } 

In subclause 14.8.2.5 [temp.deduct.type] paragraph 10, deduction from a function type considers P/A pairs from the parameter-type-list only where the "P" function type has a parameter. Deduction is not specified to fail if there are additional parameters in the corresponding "A" function type.

Notes from the September, 2013 meeting:

CWG agreed that this example should not be accepted. The existing rules seem to cover this case (deduction is not specified to “succeed,” so it's a reasonable conclusion that it fails), but it might be helpful to be clearer.




2235. Partial ordering and non-dependent types

Section: 14.8.2.5  [temp.deduct.type]     Status: open     Submitter:     Date: Richard Smith

[Detailed description pending.]




2219. Dynamically-unreachable handlers

Section: 15.3  [except.handle]     Status: open     Submitter: 2016-01-04     Date: Richard Smith

[Detailed description pending.]




2216. Exception specifications in unevaluated contexts

Section: 15.4  [except.spec]     Status: open     Submitter: Daniel Krügler     Date: 2015-12-21

[Detailed description pending.]




596. Replacing an exception object

Section: 15.5.2  [except.unexpected]     Status: open     Submitter: Alisdair Meredith     Date: 12 September 2006

When a function throws an exception that is not in its exception-specification, std::unexpected() is called. According to 15.5.2 [except.unexpected] paragraph 2,

If [std::unexpected()] throws or rethrows an exception that the exception-specification does not allow then the following happens: If the exception-specification does not include the class std::bad_exception (18.8.3 [bad.exception]) then the function std::terminate() is called, otherwise the thrown exception is replaced by an implementation-defined object of the type std::bad_exception, and the search for another handler will continue at the call of the function whose exception-specification was violated.

The “replaced by” wording is imprecise and undefined. For example, does this mean that the destructor is called for the existing exception object, or is it simply abandoned? Is the replacement in situ, so that a pointer to the existing exception object will now point to the std::bad_exception object?

Mike Miller: The call to std::unexpected() is not described as analogous to invoking a handler, but if it were, that would resolve this question; it is clearly specified what happens to the previous exception object when a new exception is thrown from a handler (15.1 [except.throw] paragraph 4).

This approach would also clarify other questions that have been raised regarding the requirements for stack unwinding. For example, 15.5.1 [except.terminate] paragraph 2 says that

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called.

This requirement could be viewed as in conflict with the statement in 15.5.2 [except.unexpected] paragraph 1 that

If a function with an exception-specification throws an exception that is not listed in the exception-specification, the function std::unexpected() is called (D.6 [exception.unexpected]) immediately after completing the stack unwinding for the former function.

If it is implementation-defined whether stack unwinding occurs before calling std::terminate() and std::unexpected() is called only after doing stack unwinding, does that mean that it is implementation-defined whether std::unexpected() is called if there is ultimately no handler found?

Again, if invoking std::unexpected() were viewed as essentially invoking a handler, the answer to this would be clear, because unwinding occurs before invoking a handler.




2002. White space within preprocessing directives

Section: 16  [cpp]     Status: open     Submitter: Richard Smith     Date: 2014-09-10

According to 16 [cpp] paragraphg 4,

The only white-space characters that shall appear between preprocessing tokens within a preprocessing directive (from just after the introducing # preprocessing token through just before the terminating new-line character) are space and horizontal-tab (including spaces that have replaced comments or possibly other white-space characters in translation phase 3).

The effect of this restriction is unclear, however, since translation phase 3 is permitted to transform all white space characters and comments into spaces. The relationship between these two rules should be clarified.




925. Type of character literals in preprocessor expressions

Section: 16.1  [cpp.cond]     Status: open     Submitter: Michael Wong     Date: 29 June, 2009

According to 16.1 [cpp.cond] paragraph 4,

The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 5.20 [expr.const] using arithmetic that has at least the ranges specified in 18.3 [support.limits], except that all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (_N3035_.18.4.2 [stdinth]). This includes interpreting character literals, which may involve converting escape sequences into execution character set members.

Ordinary character literals with a single c-char have the type char, which is neither a signed nor an unsigned integer type. Although 4.6 [conv.prom] paragraph 1 is clear that char values promote to int, regardless of whether the implementation treats char as having the values of signed char or unsigned char, 16.1 [cpp.cond] paragraph 4 isn't clear on whether character literals should be treated as signed or unsigned values. In C99, such literals have type int, so the question does not arise. If an implementation in which plain char has the values of unsigned char were to treat character literals as unsigned, an expression like '0'-'1' would thus have different values in C and C++, namely -1 in C and some large unsigned value in C++.




2190. Insufficient specification of __has_include

Section: 16.1  [cpp.cond]     Status: open     Submitter: Hubert Tong     Date: 2015-10-24

[Detailed description pending.]




1625. Adding spaces between tokens in stringizing

Section: 16.3.2  [cpp.stringize]     Status: open     Submitter: Chandler Carruth     Date: 2013-02-18

Given the following input,

  #define F(A, B, C) A ## x.B ## y.C ## z
  #define STRINGIFY(x) #x
  #define EXPAND_AND_STRINGIFY(x) STRINGIFY(x)
  char v[] = EXPAND_AND_STRINGIFY(F(a, b, c))

there is implementation variance in the value of v: some produce the string "ax.by.cz" and others produce the string "ax. by. cz". Although 16.3.2 [cpp.stringize] paragraph 2 is explicit in its treatment of leading and trailing white space, it is not clear whether there is latitude for inserting spaces between tokens, as some implementations do, since the description otherwise is written solely in terms of preprocessing tokens. There may be cases in which such spaces would be needed to preserve the original tokenization, but it is not clear whether the result of stringization needs to produce something that would lex to the same tokens.

Notes from the April, 2013 meeting:

Because the preprocessor specification is primarily copied directly from the C Standard, this issue has been referred to the C liaison for consultation with WG14.




268. Macro name suppression in rescanned replacement text

Section: 16.3.4  [cpp.rescan]     Status: open     Submitter: Bjarne Stroustrup     Date: 18 Jan 2001

It is not clear from the Standard what the result of the following example should be:

#define NIL(xxx) xxx
#define G_0(arg) NIL(G_1)(arg)
#define G_1(arg) NIL(arg)
G_0(42)

The relevant text from the Standard is found in 16.3.4 [cpp.rescan] paragraph 2:

If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file's preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

The sequence of expansion of G0(42) is as follows:

G0(42)
NIL(G_1)(42)
G_1(42)
NIL(42)

The question is whether the use of NIL in the last line of this sequence qualifies for non-replacement under the cited text. If it does, the result will be NIL(42). If it does not, the result will be simply 42.

The original intent of the J11 committee in this text was that the result should be 42, as demonstrated by the original pseudo-code description of the replacement algorithm provided by Dave Prosser, its author. The English description, however, omits some of the subtleties of the pseudo-code and thus arguably gives an incorrect answer for this case.

Suggested resolution (Mike Miller): Replace the cited paragraph with the following:

As long as the scan involves only preprocessing tokens from a given macro's replacement list, or tokens resulting from a replacement of those tokens, an occurrence of the macro's name will not result in further replacement, even if it is later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

Once the scan reaches the preprocessing token following a macro's replacement list — including as part of the argument list for that or another macro — the macro's name is once again available for replacement. [Example:

    #define NIL(xxx) xxx
    #define G_0(arg) NIL(G_1)(arg)
    #define G_1(arg) NIL(arg)
    G_0(42)                         // result is 42, not NIL(42)

The reason that NIL(42) is replaced is that (42) comes from outside the replacement list of NIL(G_1), hence the occurrence of NIL within the replacement list for NIL(G_1) (via the replacement of G_1(42)) is not marked as nonreplaceable. —end example]

(Note: The resolution of this issue must be coordinated with J11/WG14.)

Notes (via Tom Plum) from April, 2004 WG14 Meeting:

Back in the 1980's it was understood by several WG14 people that there were tiny differences between the "non-replacement" verbiage and the attempts to produce pseudo-code. The committee's decision was that no realistic programs "in the wild" would venture into this area, and trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs.




745. Effect of ill-formedness resulting from #error

Section: 16.5  [cpp.error]     Status: open     Submitter: Clark Nelson     Date: 13 November, 2008

C99 is very clear that a #error directive causes a translation to fail: Clause 4 paragraph 4 says,

The implementation shall not successfully translate a preprocessing translation unit containing a #error preprocessing directive unless it is part of a group skipped by conditional inclusion.

C++, on the other hand, simply says that a #error directive “renders the program ill-formed” (16.5 [cpp.error]), and the only requirement for an ill-formed program is that a diagnostic be issued; the translation may continue and succeed. (Noted in passing: if this difference between C99 and C++ is addressed, it would be helpful for synchronization purposes in other contexts as well to introduce the term “preprocessing translation unit.”)




1889. Unclear effect of #pragma on conformance

Section: 16.6  [cpp.pragma]     Status: open     Submitter: James Widman     Date: 2014-03-05

According to 16.6 [cpp.pragma] paragraph 1, the effect of a #pragma is to cause

the implementation to behave in an implementation-defined manner. The behavior might cause translation to fail or cause the translator or the resulting program to behave in a non-conforming manner.

It should be clarified that the extent of the non-conformance is limited to the implementation-defined behavior.




897. _Pragma and extended string-literals

Section: 16.9  [cpp.pragma.op]     Status: open     Submitter: Daniel Krügler     Date: 9 May, 2009

The specification of how the string-literal in a _Pragma operator is handled does not deal with the new kinds of string literals. 16.9 [cpp.pragma.op] says,

The string literal is destringized by deleting the L prefix, if present, deleting the leading and trailing double-quotes, replacing each escape sequence...

The various other prefixes should either be handled or prohibited.

Additional note (October, 2013):

If raw string literals are supported, the question of how to handle line splicing is relevant. The wording says that “the characters are processed through translation phase 3,” which is a bit ambiguous as to whether that includes phases 1 and 2 or not. It would be better to be explicit and say that the processing of phase 3 or of phases 1 through 3 is applied.




2193. numeric_limits<int>::radix and digits

Section: 18.3.2.4  [numeric.limits.members]     Status: open     Submitter: Tony Van Eerd     Date: 2015-11-02

[Detailed description pending.]




1944. New C incompatibilities

Section: C  [diff]     Status: open     Submitter: Mike Miller     Date: 2014-06-18

Some new features of C++ not only introduce incompatibilities with previous versions of C++ but also with C; however, the organization of Annex C [diff] makes it difficult to specify that a given feature is incompatible with both languages, and the practice has been only to document the C++ incompatibilities. Some means of specifying both sets of incompatibilities should be found, hopefully without excessive duplication between the C and C++ sections.




1248. Updating Annex C to C99

Section: C.1  [diff.iso]     Status: open     Submitter: Jonathan Wakely     Date: 2011-02-28

The description of incompatibilities with C in Annex C.1 [diff.iso] refers to C89, but there are a number of new features in C99 that should be covered.






Issues with "Concurrency" Status


1842. Unevaluated operands and “carries a dependency”

Section: 1.10  [intro.multithread]     Status: concurrency     Submitter: Hans Boehm     Date: 2014-01-23

According to 1.10 [intro.multithread] paragraph 9,

An evaluation A carries a dependency to an evaluation B if

The intent is that this does not apply to the second operands of such operators if the first operand is such that they are not evaluated, but the wording is not clear to that effect. (A similar question applies to the non-selected operand of the conditional operator ?:.)

Notes from the October, 2015 meeting:

It appears likely that the text involved will be removed by a revision to the memory_order_consume specification.




2046. Incomplete thread specifications

Section: 1.10  [intro.multithread]     Status: concurrency     Submitter: Dinka Ranns     Date: 2014-11-17

Given the example:

    auto id1 = std::this_thread::get_id();
    thread_local auto id2 = std::this_thread::get_id();
    int main()
    {

      auto id3 = std::this_thread::get_id();
      auto id4 = std::this_thread::get_id();
      std::cout << id1 << std::endl << id2 << std::endl << id3 <<
          std::endl << id4 << std::endl;
    }
  1. What is the thread of execution that initializes id1? Is it the same as, different from, or unspecified in relation to id3?

  2. I believe this is unspecified by omission. (I can find no wording in the standard saying otherwise.) It looks like the standard attempts to require that these happen in the same thread, but fails to do so. We say that in a program that does not start a thread, all ordered dynamic initializations are sequenced, which implies they happen in the same thread — because “sequenced before” is an intra-thread notion — but we do not say that these initializations are sequenced before entering main (we just say they may be “done before” entering main). This seems like a defect to me.

  3. What is the thread of execution that initializes id2? Is it the same as, different from, or unspecified in relation to id1 and id3?

  4. This also seems to be unspecified; this seems like a defect. We say in 3.6.2 [basic.start.static] paragraph 5:

    It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (3.2 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

    but “done before” and “occurs before” are meaningless noise here. I think what is intended is that these are a “sequenced before” relation for initialization of thread storage duration entities (implying initialization in the same thread) and “happens before” for initialization of static storage duration entities (implying initialization in some thread but visible to this thread).

  5. Am i guaranteed to have only one copy of id2, or could there be more?

  6. You are guaranteed to have one per thread, per 3.7.2 [basic.stc.thread] paragraph 1.

    I cannot find any explicit guarantee that the implementation will not create additional threads behind your back, so there appears to be no guarantee that you have only one id2 variable. That may be a defect.

I'm also concerned by 3.6.2 [basic.start.static] paragraph 2:

...Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit...

This is obviously wrong if a translation unit contains both static and thread storage duration variables.

Notes from the October, 2015 meeting:

A paper is required to address these issues fully. A lot of the existing text talks about sequencing when it should refer to “the transitive subset of happens before,” which could be, but is not currently, defined. “Happens before” would do if we didn't have memory_order_consume. The SG1 consensus on what should be said includes the following:




2159. Lambda capture and local thread_local variables

Section: 5.1.5  [expr.prim.lambda]     Status: concurrency     Submitter: Richard Smith     Date: 2015-07-15

Consider the following example:

  void f() {
    thread_local int n = 10;
    std::thread([&] { std::cout << n << std::endl; }).join();
  }

This function prints 0, because:

  1. The lambda does not capture n

  2. n is not initialized on the spawned thread prior to the invocation of the lambda.




1784. Concurrent execution during static local initialization

Section: 6.7  [stmt.dcl]     Status: concurrency     Submitter: Jens Maurer     Date: 2013-09-27

Regarding initialization of a block-scope static variable, 6.7 [stmt.dcl] paragraph 4 says,

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

This specification does not use the terminology of 1.10 [intro.multithread], so the meaning of “wait” is not clear. For example, will a concurrent thread that “waited” see (in the sense of happens-before) the result of the initialization (including side effects caused during the initialization)?

Perhaps the “synchronizes-with” terminology could be used here.