Lỗi unterminated if n def block from line

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 "," "," "," "," and "." [See 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:

  • Closed Issues List, which contains the issues which the Committee has decided are not defects in the International Standard, including a brief rationale explaining the reason for the decision.
  • Defect Reports List, which contains the issues that have been categorized by the Committee as Defect Reports, as well as other issues accepted by the Committee, along with their proposed resolutions.
  • Table of Contents, which contains a summary listing of all issues in numerical order.
  • Index by Section, which contains a summary listing of all issues arranged in the order of the sections of the Standard with which they deal most directly.
  • Index by Status, which contains a summary listing of all issues grouped by status.

Section references in this document reflect the section numbering of document WG21 N4958.

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:2020 document and corrected defects in the earlier 2017, 2014, 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 //www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2020, 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 //isocpp.org/std.

Revision History

Revision 112, 2023-08-18:

Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . Closed issues , , , and . Reflected the decisions of the June, 2023 plenary meeting. Reflected the deliberations of the teleconferences up to 2023-07-14.

Revision 111, 2023-03-24:

Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . Reflected the deliberations of the teleconferences up to 2023-03-03. Reflected the decisions of the February, 2023 plenary meeting.

Revision 110, 2022-11-27:

Reflected publication of the C++23 CD N4917. Closed issue . Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Created issues for the handling of NB comments on the Committee Draft: , , , , , , , , , , , , , , . Reflected the decisions of the November, 2022 plenary meeting.

Revision 109, 2022-08-07:

Added links for section references. Reset status of issues assigned to people no longer active on the committee. Reflected the deliberations of the teleconferences. Reflected the decisions of the July, 2022 plenary meeting. Added detailed descriptions to issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . Closed issue and as duplicates. Closed issues , , , , , , , , , , , , , , , and . Dissolved the "concurrency" status in favor of liaison indications, affecting issues and . Reopened issues and after obtaining EWG guidance. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 108, 2022-01-25:

Reflected the deliberations of the January, 2022 teleconferences and the decisions of the February, 2022 plenary meeting. Added new issues , , , , , , , , , , , , and .

Revision 107, 2021-12-09:

Updated the status of issues and . Reflected the deliberations of the December, 2021 CWG teleconference. Added new issues , , , , , , , , , , , and .

Revision 106, 2021-11-22:

Reflected the deliberations of CWG teleconferences from September through November, 2021, and the decisions of the October, 2021 plenary meeting. Added new issues , , , and .

Revision 105, 2021-06-08:

Reflected the deliberations of CWG teleconferences from March through August, 2021, and the decisions of the June, 2021 plenary meeting. Added new issues , , , , , , , , , , , , , , , , , , , , and .

Revision 104, 2021-02-24:

Reflected the decisions of the February, 2021 meeting. Updated the status of issues and , which were previously resolved by editorial action and adoption of a paper, respectively. Added new issue .

Revision 103, 2021-02-17:

Updated the status of a number of issues that were resolved or rendered moot by other issue resolutions or papers, including issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . Reflected the deliberations of the February, 2021 teleconference. Added new issues , , , , , , , , , and .

Revision 102, 2020-12-15: Changed back to "drafting" status after problems were found with the proposed resolution. Fixed transcription errors in the proposed resolution for . Corrected the date for resolutions approved at the February, 2019 meeting [they previously indicated approval at the “November, 2019” meeting]. Reflected actions at committee meetings and teleconferences. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 101, 2019-02-27: Reflected actions at committee meetings and teleconferences. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and ,.

Revision 100, 2018-04-11: Reflected actions at the March, 2018 committee meeting and the April, 2018 teleconference. Added new issues , , , , , , , , and .

Revision 99, 2018-02-27: Reflected actions of committee meetings and teleconferences. Added new C++17 status and moved issues incorporated in that IS to have that status. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 98, 2017-03-20: Reflected the deliberations of the February-March, 2017 Committee meeting.

Revision 97, 2017-02-07: Reflected the deliberations of the 2016 Committee meetings and teleconferences. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . [Note that a number of the new issues have only titles at this point; full descriptions will be added in the next revision of the list.]

Revision 96, 2016-02-15: Changed the status of from "ready" to "tentatively ready" to reflect revisions to accommodate changes in the underlying wording after the October, 2015 meeting. Reflected the results of drafting review teleconferences held 2016-01-11 and 2016-02-08.

Revision 95, 2015-11-10: Reflected the deliberations of the October, 2015 Committee meeting. Moved back to "drafting" status; it was incorrectly inadvertently accepted as a DR at the November, 2014 meeting.XS was moved back to drafting to allow application to additional text. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 94, 2015-05-25: Reflected deliberations of the May, 2015 meeting. Moved issues and to "extension" status, reflecting EWG's request to develop a position on trivial special functions before CWG addresses them. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 93, 2015-04-13: Incorporated EWG decisions on issues , , , , , , , , , , , , , , , and . Moved to "accepted" status, reflecting adoption of paper N4266 at the Urbana meeting. Moved to "DRWP" status, reflecting approval of N4140 as the working paper at the November, 2014 meeting. Incorporated resolutions from the drafting review teleconference held 2015-04-06.

Revision 92, 2014-11-24: Changed all issues approved by the Committee since the April, 2013 Committee Draft to "C++14" status, to reflect the adoption of ISO/IEC 14882:2014. Moved issues , , , and to "drafting" status, to "open" status, and , , , , and to "review" status in light of concerns that were raised with their proposed resolutions. Moved to "CD3" status to reflect the fact that it had been addressed by paper N3638, adopted at the April, 2013 [Bristol] meeting. Closed issues and as duplicates. Reflected the actions of the November, 2014 [Urbana] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

Revision 91, 2014-10-13: Incorporated deliberations of drafting review teleconferences held 2014-07-14 and 2014-10-06. Added new issue .

Revision 90, 2014-07-07: was returned to "drafting" status in light of an alternative suggestion for its resolution. was closed as a duplicate of . Reflected the deliberations of CWG at the 2014-06 [Rapperswil] meeting. Added new issues , , , , , , , , , , , , , , and .

Revision 89, 2014-05-27: Issues , , , and were returned to "review" status for further discussion. Restored to "ready"; it had incorrectly been moved back to "drafting" because of a misunderstood comment. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .

  • Revision 88, 2014-03-03: Changed the "FDIS" status to "C++11", reflecting having voted out a new DIS. Changed issues in "WP" and "DRWP" status to "CD3", reflecting the fact that their resolutions were contained in the Committee Draft approved for balloting at the April, 2013 meeting. Changed the status of to "CD3" when it was noticed that it was resolved by the resolution of . Changed the status of to "CD3" when it was noticed that it was resolved by the resolution of . Reflected the deliberations of the February, 2014 [Issaquah] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 87, 2014-01-20: Incorporated the results of the drafting review teleconferences held 2013-11-25 and 2014-01-13. Changed status of to "extension", reflecting the consensus of the 2012-08-13 drafting review teleconference. Moved to "WP" status because it was rendered moot by the adoption of N3652 at the April, 2013 meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 86, 2013-10-14: Reflected the deliberations of the September, 2013 [Chicago] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 85, 2013-09-03: Incorporated the results of the drafting review teleconferences held 2013-06-24 and 2013-08-26. Changed the status of to "DR" because it was [silently] addressed by N3624. Changed the status of to "DR" because it had been inadvertently overlooked in recording the motions of the April, 2013 meeting. Changed the status of to "accepted" because it is moot after the adoption of N3652. Returned to "open" status in light of additional discussion. Added new information to the description of . Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 84, 2013-05-03: Incorporated the results of the drafting review teleconference held 2013-03-25 and the deliberations of the April, 2013 [Bristol] meeting. Changed to "dup" status. Changed the status of to "drafting" status, although it was approved as a DR at the meeting, because its resolution is to be given by another resolution that was not moved. Similarly, issues and were incorrectly moved as defect reports and have been returned to "review" status awaiting the adoption of . Also, was moved as a DR but was lacking the proposed resolution in the document in the pre-meeting mailing, so it has been returned to "ready" status to be moved at the next meeting. Changed the resolutions of issues and to correct clerical errors transcribing the original proposed resolutions. Began the practice of marking proposed resolutions as "[SUPERSEDED]" when a newer one is added. Added new issues , , , , , , , , , , , , , , and .
  • Revision 83, 2013-03-18: Incorporated the results of the drafting review teleconference held 2013-02-04, including moving to "NAD" status. Moved issues and back to "review" status in light of new questions. Updated the “more information” link to refer to isocpp.org. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 82, 2013-01-14: Incorporated the results of the drafting review teleconference held 2012-12-10. Moved issues , , , , , and to "review" status to allow further consideration in light of subsequent discussion. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 81, 2012-11-03: Reflected the deliberations of the October, 2012 [Portland] meeting. Added new issues , , , , , , , , and .
  • Revision 80, 2012-09-24: Added remaining descriptions omitted from issues added in revision 77. Corrected status of issues , , , and . Changed the status of issues , , , , and in light of additional discussion. Incorporated the results of the drafting review teleconference held 2012-08-13. Moved to "dup" status. Moved to "open" status after the discussion in "dup" . Added additional examples to . Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 79, 2012-02-27: Reflected deliberations at the February, 2012 [Kona] meeting. Added descriptions omitted from some issues added in revision 77. Moved to "NAD" status. Moved to "dup" status [with respect to ]. Moved to "dup" status [with respect to ]. Added a new status, "concurrency", to track issues that will be considered and resolved by the Concurrency Working Group. Added new issues , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 78, 2012-01-17: Reflected the results of the December 5, 2011 teleconference. Changed the status of issues , , , , , , and from "ready" to "review" in response to further discussion of the proposed resolutions. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 77, 2011-09-06: Reflected the deliberations of the August, 2011 [Bloomington] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and . [Note that many of the preceding issues are missing descriptions. These will be provided in the next revision of the issues list.]
  • Revision 76, 2011-04-10: Reflected the results of the March 7, 2011 teleconference and the March, 2011 [Madrid] meeting, including adding a new status, "FDIS," for issues that are resolved in the draft advanced at that meeting for FDIS balloting. In respect for ISO rules governing work during the balloting process, no new issues have been included in this version of the list.
  • Revision 75, 2011-02-28: Reflected results of two between-meetings teleconferences, including moving a number of issues to "tentatively ready" status. Added drafting for a number of issues and moved them to "review" status. Reopened in light of recent discussion. Moved back to "open" status after further analysis. Moved issues and to "DR" status, as they had been overlooked in the results of the November, 2010 [Batavia] meeting. Added new issues , , , , , , , , , , , , , , , , , , , and .
  • Revision 74, 2010-11-29: Reflected deliberations at the November, 2010 [Batavia] meeting. Added new issues , , , , , , , , , , , , , , , and .
  • Revision 73, 2010-10-18: Moved issues and back to "open" status for potential reconsideration. Added analysis to . Moved issues and back to "drafting" status after further discussion revealed problems with the proposed resolution approved at the August, 2010 meeting. Moved to "review" status to allow consideration of extending the resolution to cover a new but related question. Reflected results of two between-meetings teleconferences, including moving a number of issues to "tentatively ready" status. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and ,
  • Revision 72, 2010-08-23: Reflected deliberations at the August, 2010 [Rapperswil] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 71, 2010-08-07: Added new issues , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 70, 2010-08-06: Reflected results of between-meetings teleconference, including moving a number of issues to "tentatively ready" status, updating proposed resolutions, and moving issues , , , and to "drafting" status for more work. Changed to "CD2" status, which was overlooked in the results of the March, 2010 meeting. Closed issues , , and as duplicates of issues , , and , respectively.
  • Revision 69, 2010-03-29: Reflected deliberations at the March, 2010 [Pittsburgh, PA] meeting. Placed all issues with "WP" status and newly-approved "DR" issues into "CD2" status, to reflect advancing the Final Committee Draft for balloting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 68, 2010-02-16: Reflected results of between-meeting teleconferences, including moving many issues to "tentatively ready" status and incorporating draft proposed resolutions for many others. Added new issues and .
  • Revision 67, 2009-11-08: Reflected deliberations at the October, 2009 [Santa Cruz, CA] meeting. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 66, 2009-09-29: Incorporated a new status, "tentatively ready", for issues whose resolutions were reviewed and approved by teleconference between meetings. Changed to "CD1" status, as it was resolved by the resolution of . Added new issues , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 65, 2009-08-03: Reflected deliberations from the Frankfurt, Germany [July, 2009] meeting. Added a new status, "concepts," for issues related to the concepts proposal that was removed from the working document and moved all concepts-related issues to that status, effectively closing them. Changed the presentation of proposed resolutions to use background colors in addition to font effects to identify changes [i.e., inserted and deleted text]. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 64, 2009-06-19: Fixed incorrect cross-references in issues and . Added proposed resolutions for issues , , , , , , and and moved them to "review" status. Added discussion to issues , , , and . Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 63, 2009-05-06: Closed as a duplicate of . Added notes to the discussion of issues , , , , and . Moved back to "open" status with additional discussion. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 62, 2009-03-23: Reflected deliberations from the Summit, NJ [March, 2009] meeting. Marked issues directly related to National Body comments on the 2008-10 Committee Draft with a tag identifying the relevant comment; the tag is both printed in the issue body and reflected in the "Index by Status" document. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 61, 2009-02-08: Provided a reference to a paper containing a proposed resolution for issues and and moved them to "review" status. Added new issues , , , , , , , , , , , , , , , , , and .
  • Revision 60, 2008-12-09: Revised the resolution of and moved to "review" status. Added new issues , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 59, 2008-10-05: Reflected deliberations from the San Francisco [September, 2008] meeting. Placed all issues with "WP" status and newly-approved "DR" issues into "CD1" status, to reflect advancing the Committee Draft for balloting. Added new proposed resolutions to issues , , and and moved them to "review" status. Changed to "dup" status in favor of , which was approved in 2003. Added new issues , , , , , , , , , , , , , and .
  • Revision 58, 2008-08-25: Fixed some incorrect section references. Fixed the title of . Changed the status of to "WP", as the paper that resolved it was approved in April, 2007. Moved back to "review" status and added proposed wording. Added discussion to issues and . Added or revised resolutions for issues , , , , and and moved them to "review" status. Added new issues , , , , , and .
  • Revision 57, 2008-07-27: Updated the status of issues , , and to reflect actions at the June, 2008 meeting that were inadvertently omitted in the preceding revision. Added proposed wording for and moved it to "review" status. Added new issues , , , , , , and .
  • Revision 56, 2008-06-29: Reflected deliberations from the Sophia Antipolis [June, 2008] meeting. Added discussion to issues and . Added proposed resolution for . Added new issues and .
  • Revision 55, 2008-05-18: Added proposed resolutions for issues , , , , , , , , , , , , , , , , and , and moved them to "review" status. Moved to "dup" status in favor of , which was changed to "review" status in light of its connection with the initializer-list proposal. Moved issues and to "WP" status because they were resolved by the constexpr proposal. Moved to "review" status because it appears to be "NAD". Changed to "review" status [from "ready"] in light of input from WG14 regarding a similar issue. Added new issues , , , , , , , , , and .
  • Revision 54, 2008-03-17: Reflected deliberations from the Bellevue [February, 2008] meeting. Restructured references inside “definitions” sections [Clause 3 [intro.defs], _N4567_.17.3 [definitions]] because the individual definitions are not sections. Returned to "open" status [it was previously erroneously in "drafting" status]. Moved issues , , and to "review" status with a recommendation to close them as "NAD." Changed issues and back to "open" status and annotated them to indicate that they have been accepted by EWG and referred to CWG for action for C++0x. Changed issues and back to "open" status and annotated them to indicate that they have been accepted by EWG and referred to CWG for action, but not for C++0x. Closed issues , , , , and as "NAD" to reflect the recommendation of EWG. Added new issues , , , , , , , , , , , and .
  • Revision 53, 2008-02-03: Updated the proposed resolutions for issues and . Updated the status of issues and to reflect actions at the Oxford [April, 2007] meeting. Added proposed resolutions and changed the status to "review" for issues , , , , , , , , , , , , , , , , , , and . Added new issues , , , , , and .
  • Revision 52, 2007-12-09: Updated the status of issues and to reflect actions at the Toronto [July, 2007] meeting. Fixed a typographical error in the example for . Added new issues , , , , , , , , , , , , and .
  • Revision 51, 2007-10-09: Reflected deliberations from the Kona [October, 2007] meeting. Added new issues and .
  • Revision 50, 2007-09-09: Updated section reference numbers to use the numbering of the most recent working draft and added text identifying the document number of that draft at the beginning of each issues list document. Updated with discussion regarding the point at which std::uncaught_exception[] becomes false. Added new issues , , , , , , , , , and .
  • Revision 49, 2007-08-05: Reflected deliberations from the Toronto [July, 2007] meeting. Added additional discussion to issues and . Added new issues , , , , and .
  • Revision 48, 2007-06-24: Added new issues , , , , and .
  • Revision 47, 2007-05-06: Reflected deliberations from the Oxford [April, 2007] meeting. Added new issues , , , , , and .
  • Revision 46, 2007-03-11: Added proposed wording to and moved it to “review” status. Added new issues , , , , , , , , , , , , , and .
  • Revision 45, 2007-01-14: Changed the status of from TC1 to WP. Added new issues , , , , , , , , and .
  • Revision 44, 2006-11-05: Reflected deliberations from the Portland [October, 2006] meeting. Added proposed wording for issues , , and , and moved them to “review” status. Added new issues , , , , , , and .
  • Revision 43, 2006-09-09: Updated issues , , and with additional discussion and proposed resolutions and moved them to “review” status; added issues , , , , , , and .
  • Revision 42, 2006-06-23: Updated issues and with additional discussion; added a reference to a paper on the topic to ; and added issues , , , , , , , , , , and .
  • Revision 41, 2006-04-22: Reflected deliberations from the Berlin [April, 2006] meeting. Added issues , , , , , , , , , , , , , , , and .
  • Revision 40, 2006-02-24: Updated to refer to paper J16/06-0022 = WG21 N1952 for its resolution. Updated to note the need for additional drafting. Reopened and broadened its scope to include object declarations as well. Updated [in “extension” status] with additional commentary. Added issues , , , , , , , , , and .
  • Revision 39, 2005-12-16: Updated with additional discussion. Added issues , , , , , , , , , , , , , and .
  • Revision 38, 2005-10-22: Reflected deliberations from the Mont Tremblant [October, 2005] meeting. Added isues , , , , , , , and .
  • Revision 37, 2005-08-27: Added issues , , , , , , and .
  • Revision 36, 2005-06-27: Reopened for additional discussion. Added issues , , , , , and .
  • Revision 35, 2005-05-01: Reflected deliberations from the Lillehammer [April, 2005] meeting. Updated issues and with additional discussion. Added new issues , , , , , , , , , , , , and .
  • Revision 34: 2005-03-06: Closed as NAD; updated issues , , , , and with additional discussion; and added new issues , , , , , and .
  • Revision 33: 2005-01-14: Updated with additional discussion. Added new issues , , , , , , , , , , , , and .
  • Revision 32: 2004-11-07: Reflected deliberations from the Redmond [October, 2004] meeting. Added new issues , , , , , , , , , and .
  • Revision 31: 2004-09-10: Updated with comments from WG14; added comments and changed the status of back to “open”; added discussion to issues , , , , and ; and added new issues , , , , and .
  • Revision 30: 2004-04-09: Reflected deliberations from the Sydney [March, 2004] meeting. Added issues 461-469, updated issues , , , , , , , , , , , , , , , and .
  • Revision 29: 2004-02-13: Added issues 441-460, updated issues , , , , , , , and .
  • Revision 28: 2003-11-15: Reflected deliberations from the Kona [October, 2003] meeting. Added issues 435-438.
  • Revision 27: 2003-09-19: Added new issues 412-434, updated , , , , , and .
  • Revision 26: 2003-04-25: Reflected deliberations from the Oxford [April, 2003] meeting. Added new issues 402-411.
  • Revision 25: 2003-03-03: Added new issues 390-401, updated .
  • Revision 24: 2002-11-08: Reflected deliberations from the Santa Cruz [October, 2002] meeting. Added new issues 379-389.
  • Revision 23: 2002-09-10: Added new issues 355-378, updated and .
  • Revision 22: 2002-05-10: Reflected deliberations from the Curacao [April, 2002] meeting. Added issues 342-354.
  • Revision 21: 2002-03-11: Added new issues 314-341, updated issues , , , , , , .
  • Revision 20: 2001-11-09: Reflected deliberations from the Redmond [October, 2001] meeting. Added issue .
  • Revision 19: 2001-09-12: Added new issues 289-308, updated issues , , .
  • Revision 18: 2001-05-19: Reflected deliberations from the Copenhagen [April, 2001] meeting. Added new issues 282-288.
  • Revision 17: 2001-04-29: Added new issues 276-81.
  • Revision 16: 2001-03-27: Updated to discuss the interaction of using-declarations and "forward declarations." Noted a problem with the proposed resolution of . Added some new discussion to . Added proposed resolution for . Updated address of C++ FAQ. Added new issues 265-275.
  • Revision 15: 2000-11-18: Reflected deliberations from the Toronto [October, 2000] meeting; moved the discussion of empty and fully-initialized const objects from into ; added new issues 254-264.
  • Revision 14: 2000-10-21: Added issues 246-252; added an extra question to and changed its status back to "review."
  • Revision 13: 2000-09-16: Added issues 229-245; changed status of to "review" because of problem detected in proposal; added wording for issues and and moved to "review" status; updated discussion of issues , , , , and .
  • Revision 12: 2000-05-21: Reflected deliberations from the Tokyo [April, 2000] meeting; added new issues 222-228.
  • Revision 11, 2000-04-13: Added proposed wording and moved issues , , , , , , , , and to "review" status. Moved from "extension" to "open" status because of recent additional discussion. Added new issues 217-221.
  • Revision 10, 2000-03-21: Split the issues list and indices into multiple documents. Added further discussion to issues and . Added proposed wording and moved issues , , , , , , , and to "review" status. Added new issues 207-216.
  • Revision 9, 2000-02-23: Incorporated decisions from the October, 1999 meeting of the Committee; added issues 174 through 206.
  • Revision 8, 1999-10-13: Minor editorial changes to issues and ; updated issue to include a related question; added issues , , , , and .
  • Revision 7, 1999-09-14: Removed unneeded change to 13.9.4 [] paragraph 9 from ; changed to refer to 6.5.6 [basic.lookup.elab]; added issues , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and .
  • Revision 6, 1999-05-31: Moved to "dup" status; added proposed wording and moved to "review" status; updated with additional question; added issues , , , , , , , , , , , and .
  • Revision 5, 1999-05-24: Reordered issues by status; added revision history; first public version.

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, 2016 WG21 meeting.

C++17: a DR/DRWP or Accepted/WP issue not resolved in CD4 but included in ISO/IEC 14882:2017.

CD5: A DR/DRWP or Accepted/WP issue not resolved in C++17 but included in the Committee Draft advanced for balloting at the July, 2019 WG21 meeting.

C++20: a DR/DRWP or Accepted/WP issue not resolved in CD5 but included in ISO/IEC 14882:2020.

CD6: A DR/DRWP or Accepted/WP issue not resolved in C++20 but included in the Committee Draft advanced for balloting at the July, 2022 WG21 meeting.

C++23: a DR/DRWP or Accepted/WP issue not resolved in CD6 but included in ISO/IEC 14882:2023.

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.

Overview

Section Issue Status Liaison Title 3 intro.defs open Definition of “argument” 3 intro.defs open 'user-declared' is not defined 4.1 intro.compliance open Requirements for freestanding implementations 4.1.1 intro.compliance.general review WG14

error disallows existing implementation practice 4.1.1 intro.compliance.general open Substitution failure and implementation limits 5.2 lex.phases ready Files ending in \ 5.2 lex.phases open SG12 Undefined behavior when splicing results in a universal-character-name 5.2 lex.phases ready Cannot depend on an already-deleted splice 5.3 lex.charset open Restrictions on the ordinary literal encoding 5.4 lex.pptoken drafting Are new/delete identifiers or preprocessing-op-or-punc? 5.4 lex.pptoken drafting Line endings in raw string literals 5.4 lex.pptoken open SG12 Undefined behavior when lexing unmatched quotes 5.5 lex.digraph review EWG Alternative tokens appearing as attribute-tokens 5.6 lex.token drafting punctuator referenced but not defined 5.12 lex.operators drafting Definition of operator and punctuator 5.13 lex.literal review editor Definition of “literal” and kinds of literals 5.13.4 lex.fcon open EWG Excess-precision floating-point literals 5.13.5 lex.string open Repeated evaluation of a string-literal may yield different objects 5.13.9 lex.ext open user-defined-integer-literal overflow 5.13.9 lex.ext drafting Multicharacter user-defined character literals 5.13.9 lex.ext drafting Out-of-range literals in user-defined-literals 6.1 basic.pre drafting Nomenclature for variable vs reference non-static data member 6.3 basic.def.odr open Is a potentially-evaluated expression in a template definition a “use?” 6.3 basic.def.odr review ODR vs alternative tokens 6.3 basic.def.odr open Dependent odr-use in generic lambdas 6.4.1 basic.scope.scope open Overloading virtual functions and functions with trailing requires-clauses 6.4.1 basic.scope.scope open Use of placeholders affecting name mangling 6.5.1 basic.lookup.general drafting Lookup for enumerators in modules 6.5.2 class.member.lookup open Definition of "ambiguous base class" missing 6.5.5.1 basic.lookup.qual.general drafting Template parameters in member selections 6.6 basic.link open Programs and translation units 6.6 basic.link open Repeated structured binding declarations 6.7.1 intro.memory open Data races and common initial sequence 6.7.2 intro.object drafting Size of base class subobject 6.7.2 intro.object drafting std::launder and reuse of character buffers 6.7.2 intro.object open Creation of objects by typeid 6.7.2 intro.object drafting Implicit object creation vs constant expressions 6.7.2 intro.object open Multiple objects of the same type at the same address 6.7.2 intro.object tentatively ready Storage reuse for string literal objects and backing arrays 6.7.2 intro.object open Address comparisons between potentially non-unique objects during constant evaluation 6.7.3 basic.life open Can cast to virtual base class be done on partially-constructed object? 6.7.3 basic.life drafting Type consistency and reallocation of scalar types 6.7.3 basic.life drafting Member access in out-of-lifetime objects 6.7.3 basic.life open Storage deallocation during period of destruction 6.7.3 basic.life review SG12 Modifying const subobjects 6.7.3 basic.life review "Refers to allocated storage" has no meaning 6.7.3 basic.life open Replacing a complete object having base subobjects 6.7.3 basic.life review Replacing union subobjects 6.7.4 basic.indet drafting Placement new and previous initialization 6.7.5 basic.stc open Storage duration and temporaries 6.7.5 basic.stc drafting Temporary storage duration 6.7.5 basic.stc review Storage duration of implicitly created objects 6.7.5.5.2 basic.stc.dynamic.allocation drafting auto return type for allocation and deallocation functions 6.7.5.5.2 basic.stc.dynamic.allocation open Overly-restrictive rules on function templates as allocation functions 6.7.5.5.2 basic.stc.dynamic.allocation drafting Allocating memory for exception objects 6.7.5.5.3 basic.stc.dynamic.deallocation open Can a one-past-the-end pointer be invalidated by deleting an adjacent object? 6.7.5.5.3 basic.stc.dynamic.deallocation drafting Exceptions and deallocation functions 6.7.6 basic.align drafting Misaligned lvalues 6.7.7 class.temporary open Mandatory copy elision vs non-class objects 6.7.7 class.temporary open Lifetime extension through static_cast 6.8 basic.types open WG14 signed char underlying representation for objects 6.8 basic.types drafting Array vs sequence in object representation 6.8.2 basic.fundamental open Floating-point zero 6.8.2 basic.fundamental open How many signed integer types are there? 6.8.2 basic.fundamental review Are cv-qualified std::nullptr_t fundamental types? 6.8.4 basic.compound open Address of past-the-end of a potentially-overlapping subobject 6.9.1 intro.execution open The definition of “sequenced before” is too narrow 6.9.2 intro.multithread open SG1 Unevaluated operands and “carries a dependency” 6.9.2.2 intro.races open Unclear specification of atomic operations 6.9.2.2 intro.races open SG1 Actions and expression evaluation 6.9.2.2 intro.races review Visible side effects and initial value of an object 6.9.3.2 basic.start.static open Interleaving of constructor calls 6.9.3.2 basic.start.static open Side effects in dynamic/static initialization 6.9.3.2 basic.start.static open Initialization order of thread_local template static data members 6.9.3.2 basic.start.static drafting odr-use and delayed initialization 6.9.3.2 basic.start.static drafting Thread storage duration and order of initialization 6.9.3.3 basic.start.dynamic drafting Constant expressions in initialization odr-use 6.9.3.3 basic.start.dynamic open thread_local dynamic initialization 7.3.6 conv.qual open Problems in the specification of qualification conversions 7.5.4 expr.prim.id drafting Unclear relationship among name, qualified name, and unqualified name 7.5.4.2 expr.prim.id.unqual review "denotes a destructor" is missing specification 7.5.4.3 expr.prim.id.qual review Implicitly moving the operand of a throw-expression in unevaluated contexts 7.5.4.4 expr.prim.id.dtor drafting Parentheses in pseudo-destructor calls 7.5.5.2 expr.prim.lambda.closure review Conversion to function pointer for lambda with explicit object parameter 7.5.5.3 expr.prim.lambda.capture drafting Reference odr-use vs implicit capture 7.5.5.3 expr.prim.lambda.capture open Temporary lifetime extension for reference init-captures 7.5.7.1 expr.prim.req.general open EWG Parameter type determination in a requirement-parameter-list 7.5.7.1 expr.prim.req.general tentatively ready Invalid types in the parameter-declaration-clause of a requires-expression 7.5.7.5 expr.prim.req.nested open Nested requirement not a constant expression 7.6.1.3 expr.call open Sequencing of braced-init-list arguments 7.6.1.3 expr.call open Result of a function call 7.6.1.3 expr.call open Confusing term "this parameter" 7.6.1.3 expr.call open Calling explicit object member functions 7.6.1.4 expr.type.conv open EWG Value-initialization of array types 7.6.1.4 expr.type.conv drafting T{expr} with reference types 7.6.1.4 expr.type.conv drafting Missing complete type requirements 7.6.1.5 expr.ref drafting Class member access referring to an unrelated class 7.6.1.5 expr.ref open Accessing ambiguous subobjects 7.6.1.5 expr.ref tentatively ready Overload resolution for non-call of class member access 7.6.1.5 expr.ref open Accessing static data members via null pointer 7.6.1.6 expr.post.incr open Postfix increment/decrement with long bit-field operands 7.6.1.7 expr.dynamic.cast drafting Explicit casts to reference types 7.6.1.8 expr.typeid open Namespace for extended_type_info 7.6.1.8 expr.typeid open typeid null dereference check in subexpressions 7.6.1.9 expr.static.cast open C-style casts that cast away constness vs static_cast 7.6.1.9 expr.static.cast drafting Incorrect use of implicit conversion sequence 7.6.1.10 expr.reinterpret.cast open reinterpret_cast to reference to function types 7.6.2.2 expr.unary.op drafting Is indirection through a null pointer undefined behavior? 7.6.2.4 expr.await open co_await in a lambda-expression 7.6.2.5 expr.sizeof open Padding in class types 7.6.2.8 expr.new open Alignment requirement for new-expressions 7.6.2.8 expr.new drafting Deleted operator delete 7.6.2.8 expr.new open Deallocation function templates 7.6.2.8 expr.new tentatively ready Constructor checking in new-expression 7.6.2.8 expr.new drafting Consistency of aligned operator delete replacement 7.6.2.8 expr.new open Kind of pointer value returned by new T[0] 7.6.2.8 expr.new review Matching deallocation for uncaught exception 7.6.2.8 expr.new open Missing definition for placement allocation/deallocation function 7.6.2.8 expr.new drafting Invoking destroying operator delete for constructor failure 7.6.2.9 expr.delete open Arguments to deallocation functions 7.6.2.9 expr.delete open Evaluation of conversions in a delete-expression 7.6.2.9 expr.delete open What is "access and ambiguity control"? 7.6.4 expr.mptr.oper review Insufficient base class restriction for pointer-to-member expression 7.6.6 expr.add drafting Pointer subtraction in large array 7.6.6 expr.add drafting Pointer arithmetic in array-like containers 7.6.6 expr.add review Array prvalues and additive operators 7.6.9 expr.rel tentatively ready SG22 Treatment of "pointer to void" for relational comparisons 7.6.16 expr.cond drafting Composite reference result type of conditional operator 7.6.16 expr.cond drafting Simplifying class conversions in conditional expressions 7.6.19 expr.ass drafting Compound assignment of braced-init-list 7.6.19 expr.ass open Assignment to scalar with a braced-init-list 7.7 expr.const drafting Definition problems with constexpr functions 7.7 expr.const open Unevaluated operands are not necessarily constant expressions 7.7 expr.const open constexpr member functions in brace-or-equal-initializers 7.7 expr.const drafting Unclear meaning of “undefined constexpr function” 7.7 expr.const drafting Unclear point that “preceding initialization” must precede 7.7 expr.const open Constant expressions and order-of-eval undefined behavior 7.7 expr.const open Value-initialization and constexpr constructor evaluation 7.7 expr.const open Viable user-defined conversions in converted constant expressions 7.7 expr.const open EWG Partially initialized variables during constant initialization 7.7 expr.const open Transparently replacing objects in constant expressions 7.7 expr.const open Defaulted consteval functions 7.7 expr.const open typeid of constexpr-unknown dynamic type 7.7 expr.const open Converting consteval lambda to function pointer in non-immediate context 7.7 expr.const open Constant destruction of reference members 7.7 expr.const open Immediate forward-declared function templates 7.7 expr.const open Too many objects have constexpr-unknown type 7.7 expr.const ready Incorrect wording applied by P2738R1 7.7 expr.const open Defaulted constructor that is an immediate function 7.7 expr.const review Ignorability of [[noreturn]] during constant evaluation 7.7 expr.const open Trivial destructor does not imply constant destruction 8.6.5 stmt.ranged drafting Including for range-based for 8.7 stmt.jump drafting Order of implicit destruction vs release of automatic storage 8.7.4 stmt.return open Glvalue result of a function call 8.7.5 stmt.return.coroutine tentatively ready Unusable promise::return_void 8.8 stmt.dcl open Omitted constant initialization of local static variables 8.8 stmt.dcl open EWG Order of destruction for parameters for operator functions 8.9 stmt.ambig drafting Syntactic disambiguation and trailing-return-types 9.1 dcl.pre open Omitted typedef declarator 9.2.2 dcl.stc open Storage class specifiers in definitions of class members 9.2.2 dcl.stc open thread_local anonymous unions 9.2.6 dcl.constexpr drafting Explicit specializations and constexpr function templates 9.2.6 dcl.constexpr review Static data members redeclared as constexpr 9.2.9.2 dcl.type.cv open Unsolicited reading of trailing volatile members 9.2.9.4 dcl.type.elab open Position of friend specifier 9.2.9.4 dcl.type.elab open Avoid circularity in specification of scope for friend class declarations 9.2.9.6 dcl.spec.auto drafting Use of auto in a trailing-return-type 9.2.9.6 dcl.spec.auto drafting auto as conversion-type-id 9.2.9.6 dcl.spec.auto drafting Meaning of “placeholder type” 9.2.9.6 dcl.spec.auto review SFINAE vs undeduced placeholder type 9.2.9.6.1 dcl.spec.auto.general tentatively ready placeholder-type-specifiers and function declarators 9.3.2 dcl.name drafting abstract-pack-declarators in type-ids 9.3.3 dcl.ambig.res open EWG Ambiguity resolution for cast to function type 9.3.4.1 dcl.meaning.general open friend named by a template-id 9.3.4.3 dcl.ref drafting References may only bind to “valid” objects 9.3.4.6 dcl.fct drafting Parameter type adjustment in dependent parameter types 9.3.4.6 dcl.fct drafting Parameter type determination still not clear enough 9.3.4.6 dcl.fct open WG14 Ellipsis following function parameter pack 9.3.4.6 dcl.fct drafting Overbroad grammar for parameter-declaration 9.3.4.6 dcl.fct ready Restrictions on explicit object member functions 9.3.4.7 dcl.fct.default drafting When are default arguments parsed? 9.3.4.7 dcl.fct.default open Forward reference to default argument 9.3.4.7 dcl.fct.default drafting Default arguments in explicit instantiations 9.3.4.7 dcl.fct.default open Default arguments and function parameter packs 9.3.4.7 dcl.fct.default open Default arguments in multiple scopes / inheritance of array bounds in the same scope 9.4 dcl.init drafting Copy elision for direct-initialization with a conversion function 9.4.2 dcl.init.aggr drafting Imprecise rule for reference member initializer 9.4.2 dcl.init.aggr drafting Brace elision and array length deduction 9.4.3 dcl.init.string drafting Omitted array bound with string initialization 9.4.4 dcl.init.ref drafting References vs pointers in UDC overload resolution 9.4.4 dcl.init.ref drafting Binding an rvalue reference to a reference-unrelated lvalue 9.4.4 dcl.init.ref drafting Reference binding with ambiguous conversions 9.4.4 dcl.init.ref drafting Qualification conversion vs reference binding 9.4.4 dcl.init.ref open Cv-qualification adjustment when binding reference to temporary 9.4.4 dcl.init.ref open Clarify meaning of "bind directly" 9.4.5 dcl.init.list drafting Reference list-initialization ignores conversion functions 9.4.5 dcl.init.list open Narrowing conversions and +/- infinity 9.4.5 dcl.init.list review editor Enumeration list-initialization from the same type 9.4.5 dcl.init.list open Improve the example for initializing by initializer list 9.4.5 dcl.init.list open Guaranteed copy elision for brace-initialization from prvalue 9.5.1 dcl.fct.def.general open EWG Type of __func__ 9.5.1 dcl.fct.def.general drafting Function/variable declaration ambiguity 9.5.1 dcl.fct.def.general open EWG __func__ should be constexpr 9.5.2 dcl.fct.def.default drafting Disallowing use of implicitly-deleted functions 9.5.2 dcl.fct.def.default review Defaulted comparison operator function for non-classes 9.5.2 dcl.fct.def.default tentatively ready Clarify constexpr for defaulted functions 9.5.4 dcl.fct.def.coroutine open Exceptions thrown during coroutine startup 9.5.4 dcl.fct.def.coroutine drafting EWG Initialization of coroutine result object 9.5.4 dcl.fct.def.coroutine ready Using *this in explicit object member functions that are coroutines 9.6 dcl.struct.bind open Reference collapsing and structured bindings 9.7.1 dcl.enum drafting Out-of-class definition of member unscoped opaque enumeration 9.7.1 dcl.enum drafting Ambiguity with opaque-enum-declaration 9.8.2.2 namespace.unnamed drafting Nested unnamed namespace of inline unnamed namespace 9.9 namespace.udecl open typename in a using-declaration with a non-dependent name 9.9 namespace.udecl drafting Ineffective redeclaration prevention for using-declarators 9.11 dcl.link drafting Linkage specifications and nested scopes 9.12.1 dcl.attr.grammar drafting alignas pack expansion syntax 9.12.2 dcl.align open alignas and non-defining declarations 9.12.2 dcl.align drafting Multiple alignas specifiers 9.12.8 dcl.attr.unused review EWG Applying [[maybe_unused]] to a label 10.1 module.unit open Linkage specifications, module purview, and module attachment 10.2 module.interface drafting Visibility of enumerator names 10.3 module.import open Importing header units synthesized from source files 11.1 class.pre open Injected-class-name as a simple-template-id 11.2 class.prop open POD-structs with template assignment operators 11.2 class.prop open EWG Trivial copyability and unions with non-trivial members 11.2 class.prop open Standard layout class with empty base class also in first member 11.4 class.mem drafting Member type depending on definition of member function 11.4.1 class.mem.general open empty-declaration grammar ambiguity 11.4.1 class.mem.general open Missing disambiguation rule for pure-specifier vs. brace-or-equal-initializer 11.4.1 class.mem.general open [[no_unique_address] and common initial sequence 11.4.3 class.mfct.non.static open Transformation for unqualified-ids in address operator 11.4.4 special tentatively ready "More constrained" for eligible special member functions 11.4.5 class.ctor drafting Deleted default union constructor and member initializers 11.4.5 class.ctor drafting Constructor templates vs default constructors 11.4.5.3 class.copy.ctor drafting Cycles in overload resolution during instantiation 11.4.5.3 class.copy.ctor drafting Copy/move construction and conversion functions 11.4.5.3 class.copy.ctor drafting Lazy declaration of special members vs overload errors 11.4.5.3 class.copy.ctor drafting Defaulted copy/move constructors and UDCs 11.4.5.3 class.copy.ctor drafting Memberwise copying with indeterminate value 11.4.5.3 class.copy.ctor open Copying non-trivial objects nested within a union 11.4.6 class.copy.assign drafting Missing case for deleted move assignment operator 11.4.6 class.copy.assign drafting Virtual base classes and generated assignment operators 11.4.7 class.dtor drafting Contradictory results of failed destructor lookup 11.4.7 class.dtor drafting Polymorphic behavior during destruction 11.4.7 class.dtor open Implicitly invoking the deleted destructor of an anonymous union member 11.4.8.3 class.conv.fct open Ambiguity with requires-clause and operator-function-id 11.4.9.3 class.static.data drafting Static data members of classes with typedef name for linkage purposes 11.4.9.3 class.static.data drafting Diagnosing ODR violations for static data members 11.4.9.3 class.static.data drafting Deduced return types vs member types 11.5 class.union open Empty unions 11.5 class.union drafting Object reallocation in unions 11.5 class.union drafting Rephrasing the definition of “anonymous union” 11.5.1 class.union.general tentatively ready Implicit change of active union member for anonymous union in union 11.5.1 class.union.general open start_lifetime_as, placement-new, and active union members 11.5.2 class.union.anon open Non-defining declarations of anonymous unions 11.5.2 class.union.anon open Naming anonymous union members as class members 11.7.3 class.virtual review Overriding virtual functions, also with explicit object parameters 11.8.3 class.access.base drafting Access of indirect virtual base class constructors 11.8.4 class.friend open EWG Does befriending a class befriend its friends? 11.8.4 class.friend drafting EWG friend declarations and module linkage 11.8.5 class.protected drafting Casting across protected inheritance 11.8.5 class.protected drafting Protected access to constructors in mem-initializers 11.8.5 class.protected drafting Protected members and access via qualified-id 11.8.5 class.protected open Base class access in aggregate initialization 11.9 class.init tentatively ready Completion of initialization by delegating constructor 11.9.3 class.base.init open EWG Potentially-invoked destructors in non-throwing constructors 11.9.3 class.base.init drafting Member function calls in partially-initialized class objects 11.9.3 class.base.init drafting Temporary materialization and base/member initialization 11.9.3 class.base.init open EWG Lifetime extension for aggregate initialization 11.9.4 class.inhctor.init review Inheriting constructors from virtual base classes 11.9.5 class.cdtor drafting Unclear/missing description of behavior during construction/destruction 11.9.5 class.cdtor open Deleting or deallocating storage of an object during its construction 11.9.6 class.copy.elision open Copy elision through reference parameters of inline functions 11.10.1 class.compare.default review Access checking during synthesis of defaulted comparison operator 11.10.3 class.spaceship review Three-way comparison requiring strong ordering for floating-point types, take 2 11.10.4 class.compare.secondary review Defaulted secondary comparison operators defined as deleted 12.2.2.1 over.match.funcs.general open Type of implicit object parameter 12.2.2.2.2 over.call.func drafting Incorrect treatment of contrived object 12.2.2.2.3 over.call.object open Surrogate call template 12.2.2.2.3 over.call.object drafting Conversion to function pointer with an explicit object parameter 12.2.2.3 over.match.oper open User-defined conversions and built-in operator overload resolution 12.2.2.3 over.match.oper open Overload resolution for ! with explicit conversion operator 12.2.2.3 over.match.oper drafting Restricting selection of builtin overloaded operators 12.2.2.3 over.match.oper open Comparison templates on enumeration types 12.2.2.7 over.match.ref drafting Converting constructors in rvalue reference initialization 12.2.2.7 over.match.ref drafting Conversions to non-class prvalues in reference initialization 12.2.2.8 over.match.list drafting Impossible case in list initialization 12.2.2.8 over.match.list open Missed case for guaranteed copy elision 12.2.2.9 over.match.class.deduct open Confusing wording for deduction from a type 12.2.2.9 over.match.class.deduct drafting CTAD for alias templates and the deducible check 12.2.2.9 over.match.class.deduct drafting Nested class template argument deduction 12.2.2.9 over.match.class.deduct open Implicit deduction guides should propagate constraints 12.2.2.9 over.match.class.deduct open Class template argument deduction for aggregates with designated initializers 12.2.2.9 over.match.class.deduct open Implicit deduction guides omit properties from the parameter-declaration-clause of a constructor 12.2.4 over.match.best open List-initialization and conversions in overload resolution 12.2.4.2 over.best.ics drafting Nested brace initialization from same type 12.2.4.2.1 over.best.ics.general drafting Incorrect definition of implicit conversion sequence 12.2.4.2.1 over.best.ics.general open Implicit conversion sequence with a null pointer constant 12.2.4.2.3 over.ics.user open List-initialization sequence with a user-defined conversion 12.2.4.2.5 over.ics.ref drafting Overload resolution and invalid rvalue-reference initialization 12.2.4.2.6 over.ics.list drafting Overload resolution with temporary from initializer list 12.2.4.2.6 over.ics.list open Narrowing conversions and overload resolution 12.2.4.2.6 over.ics.list drafting Comparing user-defined conversion sequences in list-initialization 12.2.4.2.6 over.ics.list open Implicit conversion sequence from empty list to array of unknown bound 12.2.4.3 over.ics.rank open Reference-binding tiebreakers in overload resolution 12.2.4.3 over.ics.rank open Array reference vs array decay in overload resolution 12.2.4.3 over.ics.rank drafting Overload resolution for base class conversion and reference/non-reference 12.2.4.3 over.ics.rank open Incorrect implication of logic ladder for conversion sequence tiebreakers 12.3 over.over tentatively ready Overload resolution of &x.static_func 12.3 over.over review Address of overloaded function with no target 12.4 over.oper drafting Insufficient restrictions on parameters of postfix operators 12.4.3 over.binary open Overloaded comma operator with void operand 12.5 over.built open User-defined conversions and built-in operator= 12.5 over.built open Overload resolution of conversion operator templates with built-in types 12.6 over.literal open User-defined literals and extended integer types 13 temp drafting Templates and static data members 13.1 temp.pre drafting EWG extern "C" alias templates 13.2 temp.param drafting Type adjustments of non-type template parameters 13.2 temp.param drafting How similar are template default arguments to function default arguments? 13.2 temp.param drafting Parameters following a pack expansion 13.2 temp.param review Default template arguments for template members of non-template classes 13.2 temp.param open Type of id-expression denoting a template parameter object 13.3 temp.names open What is a “nested” > or >>? 13.3 temp.names drafting braced-init-list as a template-argument 13.4 temp.arg open When do the arguments for a parameter pack end? 13.4.3 temp.arg.nontype drafting Generalized template arguments and array-to-pointer decay 13.4.3 temp.arg.nontype drafting List initializer in non-type template default argument 13.4.3 temp.arg.nontype drafting Array decay vs prohibition of subobject non-type arguments 13.4.3 temp.arg.nontype drafting Template parameter initialization 13.4.4 temp.arg.template drafting Template template arguments with default arguments 13.4.4 temp.arg.template drafting Template template parameter matching and deduction 13.5.2 temp.constr.constr open Pack expansion into a non-pack parameter of a concept 13.5.2.3 temp.constr.atomic review Context of access checks during constraint satisfaction checking 13.6 temp.type drafting Alias templates and template declaration matching 13.7 temp.decls drafting Can a variable template have an unnamed type? 13.7.2.3 temp.deduct.guide open Deduction guides cannot have a trailing requires-clause 13.7.5 temp.friend open friend templates with dependent scopes 13.7.5 temp.friend open Friend declarations naming members of class templates in non-templates 13.7.5 temp.friend open Stateful metaprogramming via friend injection 13.7.6 temp.spec.partial open Partial specialization of member templates of class templates 13.7.6 temp.spec.partial drafting Type agreement of non-type template arguments in partial specializations 13.7.6 temp.spec.partial drafting Partial specialization and nullptr 13.7.6 temp.spec.partial open Partial specialization with non-deduced contexts 13.7.6.1 temp.spec.partial.general drafting Required diagnostic for partial specialization after first use 13.7.6.2 temp.spec.partial.match drafting Non-deducible parameters in partial specializations 13.7.6.4 temp.spec.partial.member drafting Out-of-class partial specializations of member templates 13.7.7.2 temp.over.link open Can function templates differing only in parameter cv-qualifiers be overloaded? 13.7.7.2 temp.over.link open Equivalent types in function template declarations 13.7.7.3 temp.func.order open More on partial ordering of function templates 13.7.7.3 temp.func.order open Partial ordering of function templates is still underspecified 13.7.7.3 temp.func.order open Issues with partial ordering 13.7.8 temp.alias drafting Equivalence of alias templates 13.7.8 temp.alias open Pack expansion into fixed alias template parameter list 13.7.8 temp.alias drafting Access and alias templates 13.7.8 temp.alias drafting Alias template specialization in template member definition 13.7.8 temp.alias drafting Equivalent but not functionally-equivalent redeclarations 13.7.8 temp.alias drafting When is an alias template specialization dependent? 13.8 temp.res open Instantiation via non-dependent references in uninstantiated templates 13.8 temp.res open Generated variadic templates requiring empty pack 13.8.1 temp.res.general drafting Problems with the omission of the typename keyword 13.8.1 temp.res.general drafting Omission of the typename keyword in a member template parameter list 13.8.1 temp.res.general open Checking of default template arguments 13.8.2 temp.local open Name hiding and template template-parameters 13.8.2 temp.local open Hiding of template parameters by base class members 13.8.3.2 temp.dep.type drafting Dependency of alias template specializations 13.8.3.2 temp.dep.type drafting Incompletely-defined class template base 13.8.3.2 temp.dep.type open Definition of current instantiation 13.8.3.2 temp.dep.type drafting Type-dependence of local class of function template 13.8.3.3 temp.dep.expr drafting Type-dependence of function template 13.8.3.3 temp.dep.expr drafting Type dependence of function-style cast to incomplete array type 13.8.3.3 temp.dep.expr tentatively ready Type dependency of placeholder types 13.8.3.4 temp.dep.constexpr open Value-dependence of requires-expressions 13.8.3.5 temp.dep.temp drafting Dependency via non-dependent base class 13.8.4 temp.dep.res drafting How can dependent names be used in member declarations that appear outside of the class template definition? 13.8.4.1 temp.point drafting Order dependencies in template instantiation 13.8.4.1 temp.point drafting Point of instantiation of a variable template specialization 13.8.4.1 temp.point drafting Point of instantiation of incomplete class template 13.8.4.1 temp.point open Implicit instantiation, destruction, and TUs 13.8.4.1 temp.point drafting Points of instantiation for constexpr function templates 13.9 temp.spec open Generic non-template members 13.9 temp.spec open Alias template specializations 13.9.2 temp.inst review Linkage of specialization vs linkage of template arguments 13.9.2 temp.inst open Indirect nested classes of class templates 13.9.2 temp.inst drafting When does default argument instantiation occur? 13.9.2 temp.inst drafting Additional contexts where instantiation is not required 13.9.2 temp.inst drafting Default argument instantiation for friends 13.9.2 temp.inst drafting Delayed pack expansion and member redeclarations 13.9.2 temp.inst drafting Instantiation of constrained non-template friends 13.9.2 temp.inst open Instantiation of default arguments in lambda-expressions 13.9.3 temp.explicit open Syntax of explicit instantiation/specialization too permissive 13.9.3 temp.explicit open What is a “use” of a class specialization? 13.9.3 temp.explicit drafting Declaration matching in explicit instantiations 13.9.3 temp.explicit drafting Explicit instantiation of constrained member functions 13.9.3 temp.explicit drafting Explicit instantiation and trailing requires-clauses 13.9.4 temp.expl.spec drafting Use of template with “explicitly-specialized” class templates 13.9.4 temp.expl.spec drafting Non-deleted explicit specialization of deleted function template 13.9.4 temp.expl.spec drafting Use of template defining member of explicit specialization 13.9.4 temp.expl.spec drafting Explicit specializations of constexpr static data members 13.10.2 temp.arg.explicit open Unusable template constructors and conversion functions 13.10.2 temp.arg.explicit drafting Explicitly-specified non-deduced parameter packs 13.10.3 temp.deduct open Deduction rules apply to more than functions 13.10.3 temp.deduct drafting “instantiation-dependent” constructs 13.10.3 temp.deduct drafting Function parameter type decay in templates 13.10.3 temp.deduct drafting Template default arguments and deduction failure 13.10.3 temp.deduct open Defining “immediate context” 13.10.3 temp.deduct review Missing description of class SFINAE 13.10.3 temp.deduct open EWG Are default argument instantiation failures in the “immediate context”? 13.10.3.1 temp.deduct.general open Partial specialization failure and the immediate context 13.10.3.1 temp.deduct.general open Lambda body SFINAE is still required, contrary to intent and note 13.10.3.1 temp.deduct.general open Substitution into template parameters and default template arguments should be interleaved 13.10.3.1 temp.deduct.general open Trailing requires-clause can refer to function parameters before they are substituted into 13.10.3.2 temp.deduct.call open Cv-qualified function types in template argument deduction 13.10.3.2 temp.deduct.call drafting initializer_list deduction failure 13.10.3.2 temp.deduct.call drafting Deducing function types from cv-qualified types 13.10.3.2 temp.deduct.call open Argument conversions to nondeduced parameter types revisited 13.10.3.3 temp.deduct.funcaddr drafting Base-derived conversion in member pointer deduction 13.10.3.5 temp.deduct.partial open Partial ordering and reference collapsing 13.10.3.5 temp.deduct.partial drafting Cv-qualification in deduction of reference to array 13.10.3.6 temp.deduct.type open Length mismatch in template type deduction 13.10.3.6 temp.deduct.type drafting Unclear presentation style of template argument deduction rules 14.2 except.throw open Unclear argument type for copy of exception object 14.4 except.handle drafting Multiple exceptions with one exception object 14.4 except.handle drafting Dynamically-unreachable handlers 14.5 except.spec open Explicit instantiation and exception specifications 15 cpp open WG14 White space within preprocessing directives 15.2 cpp.cond open Type of character literals in preprocessor expressions 15.2 cpp.cond open Interaction of constant expression changes with preprocessor expressions 15.2 cpp.cond open Insufficient specification of __has_include 15.2 cpp.cond open SG12 Undefined behavior when macro-replacing "defined" operator 15.3 cpp.include open SG12 Undefined behavior with macro-expanded

include directives 15.6 cpp.replace drafting WG14 Macro invocation spanning end-of-file 15.6 cpp.replace drafting Zero-argument macros incorrectly specified 15.6.1 cpp.replace.general open SG12 Undefined behavior for preprocessing directives in macro arguments 15.6.3 cpp.stringize open WG14 Adding spaces between tokens in stringizing 15.6.3 cpp.stringize drafting Stringizing raw string literals containing newline 15.6.3 cpp.stringize open SG12 Undefined behavior when creating an invalid string literal via stringizing 15.6.4 cpp.concat open WG14 Removing placemarker tokens and retention of whitespace 15.6.4 cpp.concat open SG12 Undefined behavior when token pasting does not create a preprocessing token 15.6.5 cpp.rescan open WG14 Macro name suppression in rescanned replacement text 15.7 cpp.line open SG12 Undefined behavior with

line 15.7 cpp.line open WG14 Escape sequences for the string-literal of

line 15.9 cpp.pragma drafting Unclear effect of

pragma on conformance 15.11 cpp.predefined open SG12 Undefined behavior for predefined macros 15.12 cpp.pragma.op open _Pragma and extended string-literals 15.12 cpp.pragma.op open WG14 string-literals of the _Pragma operator 17.13.3 csetjmp.syn open Unclear description of longjmp undefined behavior Annex B implimits drafting Normative requirements in an informative Annex Annex C diff open New C incompatibilities C.6 diff.cpp03 drafting Additional differences between C++ 2003 and C++ 2011 C.6.4 diff.cpp03.dcl.dcl open Missing Annex C entry for linkage effects of linkage-specification C.7 diff.iso open Updating Annex C to C99

Issues with "Ready" Status

1698. Files ending in \

Section: 5.2 [lex.phases] Status: ready Submitter: David Krauss Date: 2013-06-10

The description of how to handle file not ending in a newline in 5.2 [] 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.

CWG 2023-07-14

Addressed by the resolution for .

2747. Cannot depend on an already-deleted splice

Section: 5.2 [lex.phases] Status: ready Submitter: Jim X Date: 2021-09-14

[From editorial issue 4903.]

Subclause 5.2 [] paragraph 2 specifies:

... Each sequence of a backslash character [\] immediately followed by zero or more whitespace characters other than new-line followed by a new-line character is deleted, splicing physical source lines to form logical source lines. ... A source file that is not empty and that does not end in a new-line character, or that ends in a splice, shall be processed as if an additional new-line character were appended to the file.

This is confusing, because the first sentence deletes all splices, and then the last sentence checks for a splice that has already been deleted.

Proposed resolution [approved by CWG 2023-07-14]:

Change in 5.2 [] paragraph 2 as follows:

... Each sequence of a backslash character [\] immediately followed by zero or more whitespace characters other than new-line followed by a new-line character is deleted, splicing physical source lines to form logical source lines. ... A source file that is not empty and that [after splicing] does not end in a new-line character, or that ends in a splice, shall be processed as if an additional new-line character were appended to the file.

CWG 2023-07-14

CWG noted that a lone backslash at the end of a file remains [in the status quo and with the proposed change] and turns into an ill-formed preprocessing-token. The wording as amended seems sufficiently clear to consider resolved.

2755. Incorrect wording applied by P2738R1

Section: 7.7 [expr.const] Status: ready Submitter: Jens Maurer Date: 2023-06-28

P2738R1 [constexpr cast from void*: towards constexpr type-erasure] applied incorrect wording to 7.7 [] bullet 5.14:

... a conversion from a prvalue P of type ”pointer to cv void” to a pointer-to-object type T unless P points to an object whose type is similar to T;
  • ...

The issue is that T is defined to be a pointer type, but the "similar to" phrasing uses it as the pointee type.

Proposed resolution [approved by CWG 2023-07-14]:

Change in 7.7 [] bullet 5.14 as follows:

... a conversion from a prvalue P of type ”pointer to cv void” to a pointer-to-object type T "cv1 pointer to T", where T is not cv2 void, unless P points to an object whose type is similar to T;
  • ...

2553. Restrictions on explicit object member functions

Section: 9.3.4.6 [dcl.fct] Status: ready Submitter: Jens Maurer Date: 2021-12-10

Subclause 9.3.4.6 [] paragraph 6 specifies

A member-declarator with an explicit-object-parameter-declaration shall not include a ref-qualifier or a cv-qualifier-seq and shall not be declared static or virtual.

This does not address the situation when an explicit object member function becomes implicitly virtual by overriding an implicit object member function. That should be prevented.

This also does not address class-specific allocation and deallocation functions, which are implicitly static.

Proposed resolution [approved by CWG 2023-06-15] [SUPERSEDED]:

  1. Change in 9.3.4.6 [] paragraph 6 as follows:
    A member-declarator with an explicit-object-parameter-declaration shall not include a ref-qualifier or a cv-qualifier-seq and shall not be declared static or virtual.
  2. Change in 9.3.4.6 [] paragraph 7 as follows: ... An implicit object member function is a non-static member function without an explicit object parameter. [ Note: An explicit object member function cannot be virtual [11.7.3 [class.virtual]]. end note ]
  3. Add a new paragraph before 11.7.3 [] paragraph 7 as follows: A virtual function shall not be an explicit object member function [9.3.4.6 [dcl.fct]].

[ Example:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

\ end example] The ref-qualifier, or lack thereof, ...

Proposed resolution [approved by CWG 2023-07-14]:

  1. Change in 9.3.4.6 [] paragraph 6 as follows:
    A member-declarator with an explicit-object-parameter-declaration shall not include a ref-qualifier or a cv-qualifier-seq and shall not be declared static or virtual.
  2. Change in 9.3.4.6 [] paragraph 7 as follows: ... An implicit object member function is a non-static member function without an explicit object parameter. [ Note: An explicit object member function cannot be virtual [11.7.3 [class.virtual]]. end note ]
  3. Change in 11.4.11 [] paragraph 1 as follows: Any allocation function for a class T is a static member [even if not explicitly declared static]; it shall not have an explicit object parameter.
  4. Change in 11.4.11 [] paragraph 3 as follows: Any deallocation function for a class T is a static member [even if not explicitly declared static]; it shall not have an explicit object parameter.
  5. Change in 11.4.11 [] paragraph 4 as follows: [ Note: Since member allocation and deallocation functions are static they cannot be virtual. end note ]
  6. Add a new paragraph before 11.7.3 [] paragraph 7 as follows: A virtual function shall not be an explicit object member function [9.3.4.6 [dcl.fct]].

[ Example:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

-- end example]

The ref-qualifier, or lack thereof, ...

2754. Using *this in explicit object member functions that are coroutines

Section: 9.5.4 [dcl.fct.def.coroutine] Status: ready Submitter: Christof Meerwald Date: 2023-06-23

Subclause 9.5.4 [] paragraph 4 specifies:

In the following, pi is an lvalue of type Pi , where p1 denotes the object parameter and pi+1 denotes the ith non-object function parameter for a non-static member function, and pi denotes the ith function parameter otherwise. For a non-static member function, q1 is an lvalue that denotes *this; any other qi is an lvalue that denotes the parameter copy corresponding to pi , as described below.

An explicit object member function is a non-static member function, but there is no this.

Proposed resolution [approved by CWG 2023-07-14]:

Change in 9.5.4 [] paragraph 4 as follows:

In the following, pi is an lvalue of type Pi , where p1 denotes the object parameter and pi+1 denotes the ith non-object function parameter for a non-static an implicit object member function, and pi denotes the ith function parameter otherwise. For a non-static an implicit object member function, q1 is an lvalue that denotes *this; any other qi is an lvalue that denotes the parameter copy corresponding to pi, as described below.

Issues with "Tentatively Ready" Status

2753. Storage reuse for string literal objects and backing arrays

Section: 6.7.2 [intro.object] Status: tentatively ready Submitter: Brian Bi Date: 2023-06-29

Subclause 6.7.2 [] paragraph 9 specifies the general principle that two objects with overlapping lifetimes have non-overlapping storage, which can be observed by comparing addresses:

Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.

After P2752, there are two exceptions: string literal objects and backing arrays for initializer lists.

Subclause 5.13.5 [] paragraph 9 specifies:

Evaluating a string-literal results in a string literal object with static storage duration [6.7.5 [basic.stc]]. Whether all string-literals are distinct [that is, are stored in nonoverlapping objects] and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

Subclause 9.4.4 [] paragraph 5, after application of P2752R3 [approved in June, 2023], specifies:

Whether all backing arrays are distinct [that is, are stored in non-overlapping objects] is unspecified.

It is unclear whether a backing array can overlap with a string literal object.

Furthermore, it is unclear whether any such object can overlap with named objects or temporaries, for example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

Proposed resolution [approved by CWG 2023-07-14]:

  1. Add a new paragraph before 6.7.2 [] paragraph 9 and change the latter as follows:
    An object is a potentially non-unique object if it is a string literal object [5.13.5 [lex.string]], the backing array of an initializer list [9.4.4 [dcl.init.ref]], or a subobject thereof.

Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types, or if they are both potentially non-unique objects; otherwise, they have distinct addresses and occupy disjoint bytes of storage.

[Example 2:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

-- end example]

  1. Change in subclause 5.13.5 [] paragraph 9 as follows: Evaluating a string-literal results in a string literal object with static storage duration [6.7.5 [basic.stc]]. [ Note: String literal objects are potentially non-unique objects [6.7.2 [intro.object]]. Whether all string-literals are distinct [that is, are stored in nonoverlapping objects] and whether successive evaluations of a string-literal yield the same or a different object is unspecified. end note ]
  2. Change in subclause 9.4.4 [] paragraph 5, after application of P2752R3 [approved in June, 2023], as follows: Whether all backing arrays are distinct [that is, are stored in non-overlapping objects] is unspecified. [ Note: Backing arrays are potentially non-unique objects [6.7.2 [intro.object]]. end note ]

CWG 2023-07-14

CWG resolved that a named or temporary object is always disjoint from any other object, and thus cannot overlap with a string literal object or a backing array. The lines b4 and b5 in the example highlight that outcome.

Backing arrays and string literals can arbitrarily overlap among themselves; CWG believes the proposed wording achieves that outcome.

The ancillary question how address comparisons between potentially non-unique objects are treated during constant evaluation is handled in .

2565. Invalid types in the parameter-declaration-clause of a requires-expression

Section: 7.5.7.1 [expr.prim.req.general] Status: tentatively ready Submitter: Barry Revzin Date: 2022-04-07

Consider:

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

All implementations accept this translation unit. However, the rule in 7.5.7.1 [] paragraph 5 does not cover the parameter-declaration-clause::

The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

62; it does not cause the program to be ill-formed. The substitution and semantic constraint checking proceeds in lexical order and stops when a condition that determines the result of the requires-expression is encountered. If substitution [if any] and semantic constraint checking succeed, the requires-expression evaluates to

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

63.

Proposed resolution [approved by CWG 2023-06-17]:

Change in 7.5.7.1 [] paragraph 5:

The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its parameter-declaration-clause [if any] or its requirements or the violation of the semantic constraints of those requirements. In such cases, ...

2725. Overload resolution for non-call of class member access

Section: 7.6.1.5 [expr.ref] Status: tentatively ready Submitter: Richard Smith Date: 2023-04-26

Consider:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

This is ill-formed as confirmed by . Various other changes [see ] have put the following example into the same category:

struct B {

static void f[];
} y; void [*q][] = y.f; // error

If this is the intended outcome [although major implementations disagree], then the rules in 7.6.1.5 [expr.ref] should be clarified accordingly.

Proposed resolution [approved by CWG 2023-06-13]:

Change in 7.6.1.5 [] bullet 6.3 as follows:

... If E2 is an overload set, the expression shall be the [possibly-parenthesized] left-hand operand of a member function call [7.6.1.3 [expr.call]], and function overload resolution [12.2 [over.match]] is used to select the function to which E2 refers. The type of E1.E2 is the type of E2 and E1.E2 refers to the function referred to by E2. If E2 refers to a static member function, E1.E2 is an lvalue. Otherwise [when E2 refers to a non-static member function], E1.E2 is a prvalue. The expression can be used only as the left-hand operand of a member function call [11.4.2 [class.mfct]]. [Note 5: Any redundant set of parentheses surrounding the expression is ignored [7.5.3 [expr.prim.paren]]. —end note]
  • ...

This also addresses .

2102. Constructor checking in new-expression

Section: 7.6.2.8 [expr.new] Status: tentatively ready Submitter: Richard Smith Date: 2015-03-16

According to 7.6.2.8 [] paragraph 25,

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 [11.4.11 [class.free]], and the constructor [11.4.5 [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?

Additional notes [May, 2023]:

It is unclear whether default-initialization is required to be well-formed even for an array with no elements.

Proposed resolution [approved by CWG 2023-06-16]:

  1. Insert a new paragraph before 7.6.2.8 [] paragraph 9:
    If the allocated type is an array, the new-initializer is a braced-init-list, and the expression is potentially-evaluated and not a core constant expression, the semantic constraints of copy-initializing a hypothetical element of the array from an empty initializer list are checked [9.4.5 [dcl.init.list]]. [ Note: The array can contain more elements than there are elements in the braced-init-list, requiring initialization of the remainder of the array elements from an empty initializer list. -- end note ]

Objects created by a new-expression have dynamic storage duration [6.7.5.5 [basic.stc.dynamic]]. ...

  1. Change in 7.6.2.8 [] paragraph 25 as follows: 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 [6.7.5.5.3 [basic.stc.dynamic.deallocation]], and the constructor [11.4.5 [class.ctor]] selected for the initialization [if any]. If the new-expression creates an array of objects of class type, the destructor is potentially invoked [11.4.7 [class.dtor]].
  2. Change in 7.6.2.8 [] paragraph 28 as follows: A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations [9.3.4.6 [dcl.fct]], all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called. If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function [7.6.2.9 [expr.delete]]. In any case, the matching deallocation function [if any] shall be non-deleted and accessible from the point where the new-expression appears.
  3. Change in 9.4.1 [] paragraph 7 as follows:

    To default-initialize an object of type T means:

    ... If T is an array type, the semantic constraints of default-initializing a hypothetical element shall be met and each element is default-initialized. ...
  4. Change in 9.4.1 [] paragraph 9 as follows:

    To value-initialize an object of type T means:

    if If T is a [possibly cv-qualified] class type [Clause 11 [class]], then if T has either no default constructor [11.4.5.2 [class.default.ctor]] or a default constructor that is user-provided or deleted, then the object is default-initialized; otherwise, the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;. if If T is an array type, the semantic constraints of value-initializing a hypothetical element shall be met and each element is value-initialized;. otherwiseOtherwise, the object is zero-initialized.

2749. Treatment of "pointer to void" for relational comparisons

Section: 7.6.9 [expr.rel] Status: tentatively ready Submitter: lprv Date: 2023-03-12 Liaison: SG22

[From editorial issue 6173.]

Subclause 7.6.9 [] paragraph 4 and paragraph 5 specify:

The result of comparing unequal pointers to objects [ Footnote: ] is defined in terms of a partial order consistent with the following rules: ... [Note 1: A relational operator applied to unequal function pointers or to unequal pointers to void yields an unspecified result. -- end note]

Comparing pointers to objects that are stored in a variable of type "pointer to void" should be fine.

Possible resolution [approved by CWG 2023-06-16]:

Change in 7.6.9 [] paragraph 4 and paragraph 5 as follows:

The result of comparing unequal pointers to objects [ Footnote: ... ] is defined in terms of a partial order consistent with the following rules: ... [Note 1: A relational operator applied to unequal function pointers or to unequal pointers to void yields an unspecified result. A pointer value of type "pointer to cv void" can point to an object [6.8.4 [basic.compound]]. -- end note]

2556. Unusable promise::return_void

Section: 8.7.5 [stmt.return.coroutine] Status: tentatively ready Submitter: Davis Herring Date: 2022-03-24

Subclause 8.7.5 [] paragraph 3 specifies:

If p.return_void[] is a valid expression, flowing off the end of a coroutine's function-body is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine's function-body results in undefined behavior.

However, 9.5.4 [] paragraph 6 suggests:

If searches for the names return_void and return_value in the scope of the promise type each find any declarations, the program is ill-formed. [Note: If return_void is found, flowing off the end of a coroutine is equivalent to a co_return with no operand. Otherwise, flowing off the end of a coroutine results in undefined behavior [8.7.5 [stmt.return.coroutine]]. —end note]

The difference is between the conditions "valid expression" and "found by name lookup". Effectively, it means that undefined behavior might result where the implementation could instead diagnose an ill-formed use of return_void [for example, because it is inaccessible, deleted, or the function call requires arguments].

Proposed resolution [approved by CWG 2023-06-17]:

Change in 8.7.5 [] paragraph 3 as follows:

If p.return_void[] is a valid expression a search for the name return_void in the scope of the promise type finds any declarations, flowing off the end of a coroutine's function-body is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine's function-body results in undefined behavior.

2476. placeholder-type-specifiers and function declarators

Section: 9.2.9.6.1 [dcl.spec.auto.general] Status: tentatively ready Submitter: Davis Herring Date: 2021-01-29

According to 9.2.9.6.1 [] paragraph 3,

The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function declarator includes a trailing-return-type [9.3.4.6 [dcl.fct]], that trailing-return-type specifies the declared return type of the function. Otherwise, the function declarator shall declare a function.

This wording disallows a declaration like

int f[]; auto [*fp][]=f;

The requirement to declare a function was introduced by the resolution of .

Proposed resolution [April, 2021] [SUPERSEDED]:

Change 9.2.9.6.1 [] paragraph 3 as follows:

The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid if the function declarator includes a trailing-return-type T [9.3.4.6 [dcl.fct]] or declares a function. If the function declarator includes a trailing-return-type [9.3.4.6 [dcl.fct]], that trailing-return-type specifies In the former case, T is the declared return type of the function. Otherwise, the function declarator shall declare a function. If the declared return type of the a function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function [8.5.2 [stmt.if]].

Additional notes [May, 2021]:

It was observed that the proposed resolution above does not address the example in the issue, since fp neither has a trailing-return-type nor declares a function. Presumably another case in which a function declarator with a placeholder return type should be permitted is in the declaration of a variable in which the type is deduced from its initializer.

It was also noted in passing that the deduction in the example is only partial: the parameter-type-list is specified by the declarator and only the return type is deduced from the initializer. Although this example is supported by current implementations, there is implementation divergence in the support of another case in which only part of the variable's type is deduced:

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
This issue is related to , which prohibited cases like

std::vector v;
The ultimate outcome of the two issues should be:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

0

Proposed resolution [January, 2023]:

  1. Change in 9.2.9.6.1 [] paragraph 1 as follows:
    A placeholder-type-specifier designates a placeholder type that will be replaced later, typically by deduction from an initializer.
  2. Change and split 9.2.9.6.1 [] paragraph 3 as follows: A placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such for a function declarator is valid that includes a trailing-return-type [9.3.4.6 [dcl.fct]]. If the function declarator includes a trailing-return-type [9.3.4.6 [dcl.fct]], that trailing-return-type specifies the declared return type of the function.

Otherwise, the A placeholder type can appear in the decl-specifier-seq or type-specifier-seq in the declared return type of a function declarator shall declare that declares a function. If the declared return type of the function contains a placeholder type, ; the return type of the function is deduced from non-discarded return statements, if any, in the body of the function [8.5.2 [stmt.if]].

  1. Change in 9.2.9.6.1 [] paragraph 4 as follows: The type of a variable declared using a placeholder type is deduced from its initializer. This use is allowed in an initializing declaration [9.4 [dcl.init]] of a variable. The placeholder type shall appear as one of the decl-specifiers in the decl-specifier-seq and or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a decl-specifier; the decl-specifier-seq shall be followed by one or more declarators, each of which shall be followed by a non-empty initializer. [ Example:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

1 -- end example ]

  1. Change in 9.3.4.6 [] paragraph 1 as follows: In a declaration T D where D has the form

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

2 and the type of the contained declarator-id in the declaration T D1 is "derived-declarator-type-list T",:

If the trailing-return-type is present, T shall be the single type-specifier auto, and the declared return type of the function type is the type specified by the trailing-return-type. Otherwise, the declared return type of the function type is T.

theThe type of the declarator-id in D is "derived-declarator-type-list noexceptopt function of parameter-type-list cv-qualifier-seqopt ref-qualifieropt returning T U", where

the parameter-type-list is derived from the parameter-declaration-clause as described below, U is the declared return type, and
  • the optional noexcept is present if and only if the exception specification [14.5 [except.spec]] is non-throwing. The optional attribute-specifier-seq appertains to the function type.
  • Remove 9.3.4.6 [] paragraph 2: In a declaration T D where D has the form

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

3 and the type ... The optional attribute-specifier-seq appertains to the function type.

  1. Change in 11.4.8.3 [] paragraph 1 as follows: A declaration whose declarator-id has an unqualified-id that is a conversion-function-id declares a conversion function; its declarator shall be a function declarator [9.3.4.6 [dcl.fct]] of the form

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

4 where the ptr-declarator noptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms: ...

  1. Change in 11.4.8.3 [] paragraph 2 as follows: A conversion function shall have no non-object parameters and shall be a non-static member function of a class or class template X; its declared return type is the conversion-type-id and it specifies a conversion from X to the type specified by the conversion-type-id interpreted as a type-id [9.3.2 [dcl.name]]. A decl-specifier in the decl-specifier-seq of a conversion function [if any] shall not be a defining-type-specifier .
  2. Remove 11.4.8.3 [] paragraph 3: The type of the conversion function is “noexceptopt function taking no parameter cv-qualifier-seq opt ref-qualifier opt returning conversion-type-id”.

2570. Clarify constexpr for defaulted functions

Section: 9.5.2 [dcl.fct.def.default] Status: tentatively ready Submitter: Gabriel dos Reis Date: 2022-04-18

After the application of P2448R2, 9.5.2 [] paragraph 3 reads:

A function explicitly defaulted on its first declaration is implicitly inline [9.2.8 [dcl.inline]], and is implicitly constexpr [9.2.6 [dcl.constexpr]] if it satisfies the requirements for a constexpr function.

It is unclear that no other such defaulted function is implicitly constexpr.

Proposed resolution [approved by CWG 2023-06-17]:

A function explicitly defaulted on its first declaration is implicitly inline [9.2.8 [dcl.inline]], and is implicitly constexpr [9.2.6 [dcl.constexpr]] if it satisfies the requirements for a constexpr function. [Note: Other defaulted functions are not implicitly constexpr. -- end note ]

2595. "More constrained" for eligible special member functions

Section: 11.4.4 [special] Status: tentatively ready Submitter: Barry Revzin Date: 2022-06-08

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

5

Per the wording,

1 is not eligible for Foo, because the constraints are not satisfied. But

2 also is not eligible, because

1 is more constrained than

2. The intent is that

2 is eligible.

Proposed resolution [approved by CWG 2023-06-17]:

Change in 11.4.4 [] paragraph 6 as follows:

An eligible special member function is a special member function for which: the function is not deleted, the associated constraints [13.5 [temp.constr]], if any, are satisfied, and
  • no special member function of the same kind whose associated constraints, if any, are satisfied is more constrained [13.5.5 [temp.constr.order]].

2591. Implicit change of active union member for anonymous union in union

Section: 11.5.1 [class.union.general] Status: tentatively ready Submitter: Richard Smith Date: 2022-05-29

Subclause 11.5.1 [] paragraph 6 describes how union member subobjects are implicitly created by certain assignment operations that assign to union members. However, this description does not appear to properly handle the case of an anonymous union appearing within a union:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

6

Here, the expectation is that the assignment to a.y starts the lifetime of the anonymous union member subobject within A and also the int member subobject of the anonymous union member subobject. But the algorithm for computing S[a.y] determines that it is {a.y} and does not include the anonymous union member subobject.

Proposed resolution [approved by CWG 2023-06-17]:

Change in 11.5.1 [] paragraph 6 as follows:

In an assignment expression of the form E1 = E2 that uses either the built-in assignment operator [7.6.19 [expr.ass]] or a trivial assignment operator [11.4.6 [class.copy.assign]], for each element X of S[E1] and each anonymous union member X [11.5.2 [class.union.anon]] that is a member of a union and has such an element as an immediate subobject [recursively], if modification of X would have undefined behavior under 6.7.3 [basic.life], an object of the type of X is implicitly created in the nominated storage; no initialization is performed and the beginning of its lifetime is sequenced after the value computation of the left and right operands and before the assignment.

Editing note: Adding this rule into the definition of S would be more logical, but S[E] is a set of subexpressions of E and there is no form of expression that names an anonymous union member. Redefining S[E] to be a set of objects might be a better option.

2756. Completion of initialization by delegating constructor

Section: 11.9 [class.init] Status: tentatively ready Submitter: Brian Bi Date: 2023-06-20

Subclause 6.7.3 [] paragraph 1 specifies:

... The lifetime of an object of type T begins when: storage with the proper alignment and size for type T is obtained, and its initialization [if any] is complete [including vacuous initialization] [9.4 [dcl.init]], except that ...

It is unclear whether initialization is considered complete when the [ultimate] target constructor completes, or when the outermost delegating constructor completes. Subclause 14.3 [] paragraph 4 suggests it is the former:

If the compound-statement of the function-body of a delegating constructor for an object exits via an exception, the object's destructor is invoked. ...

Proposed resolution [approved by CWG 2023-07-14]:

  1. Split and change 11.9.3 [] paragraph 9 as follows:
    [Note 3: An abstract class ... end note ] An attempt to initialize more than one non-static data member of a union renders the program ill-formed. [Note 4: After the call to a constructor for class X ... end note ] [Example 6: ... end example ]

An attempt to initialize more than one non-static data member of a union renders the program ill-formed.

An object's initialization is considered complete when a non-delegating constructor for that object returns. [Note: Therefore, an object's lifetime can begin [6.7.3 [basic.life]] before all delegating constructors have completed. end note]
  1. Change in 6.7.3 [] bullet 1.2 as follows:

    ... The lifetime of an object of type T begins when:

    storage with the proper alignment and size for type T is obtained, and its initialization [if any] is complete [including vacuous initialization] [9.4 [dcl.init], 11.9.3 [class.base.init]], except that ...

1038. Overload resolution of &x.static_func

Section: 12.3 [over.over] Status: tentatively ready Submitter: Mike Miller Date: 2010-03-02

The Standard is not clear whether the following example is well-formed or not:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

7

According to 7.6.1.5 [] bullet 4.3, you do function overload resolution to determine whether x.f is a static or non-static member function. 7.6.2.2 [] 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 [12.3 [] paragraph 1] . The problem is that 12.3 [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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

8

Because the address occurs in a call context and not in one of the contexts mentioned in 12.3 [] paragraph 1, the call expression in foo is presumably ill-formed. Contrast this with the similar example

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

9

This call presumably is well-formed because 12.2.2.2 [over.match.call] applies to “the address of a set of overloaded functions.” [This was clearer in the wording prior to the resolution of : “...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 12.2.2.2 [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 12.3 [over.over] should be similarly understood as applying to member access expressions?

Additional notes [February, 2023]

This appears to be resolved, in part by P1787R6 [accepted November, 2020].

CWG 2023-06-12

The clarifications in P1787R6 did not address the core of this issue, so it is kept open. In order to avoid confusion, a wording change to clarify the treatment [regardless of direction] seems advisable. CWG felt that the first and second examples should be treated consistently, and expressed a mild preferences towards making those ill-formed. It was noted that the reference to id-expression in 12.3 [over.over] can be understood to refer to the id-expression of a class member access.

This issue is resolved by .

2600. Type dependency of placeholder types

Section: 13.8.3.3 [temp.dep.expr] Status: tentatively ready Submitter: Hubert Tong Date: 2022-06-18

Subclause 13.8.3.2 [] paragraph 7 has a list of types considered to be dependent. This list covers placeholder types only insofar as it has an entry about decltype[expression]. Subclause 13.8.3.3 [] paragraph 3 has a list of expression forms not considered dependent unless specific types named by the expressions are dependent. This list includes forms where placeholder types are allowed. For example, the wording does not say that the new-expression at

1 [below] is dependent, but it ought to be:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

0

Some implementation even treats an obviously non-dependent case as dependent:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

1

A similar example that is non-dependent:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

2

And another non-dependent one:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

3

And here is an example that is dependent:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

4

Proposed resolution [November, 2022] [SUPERSEDED]:

  1. Change in 7.6.2.8 [] paragraph 2 as follows:
    If a placeholder type [9.2.9.6 [dcl.spec.auto]] or a placeholder for a deduced class type [9.2.9.7 [dcl.type.class.deduct]] appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the allocated type is deduced as follows: Let init be the new-initializer , if any, and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration [9.2.9.6 [dcl.spec.auto]]:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

5

  1. Insert new paragraphs before 13.8.3.2 [] paragraph 7 and change the latter as follows: An initializer is dependent if any constituent expression [6.9.1 [intro.execution]] of the initializer is type-dependent. A placeholder type [9.2.9.6.1 [dcl.spec.auto.general]] is dependent if it designates a type deduced from a dependent initializer.

A placeholder for a deduced class type [9.2.9.7 [dcl.type.class.deduct]] is dependent if

it has a dependent initializer or any default template-argument of the primary class template named by the placeholder is dependent when considered in the scope enclosing the primary class template.

A type is dependent if it is

... a function type whose exception specification is value-dependent, denoted by a dependent placeholder type, denoted by a dependent placeholder for a deduced class type,
  • ...

Proposed resolution [approved by CWG 2023-06-12]:

  1. Change in 7.6.2.8 [] paragraph 2 as follows:
    If a placeholder type [9.2.9.6 [dcl.spec.auto]] or a placeholder for a deduced class type [9.2.9.7 [dcl.type.class.deduct]] appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the allocated type is deduced as follows: Let init be the new-initializer , if any, and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration [9.2.9.6 [dcl.spec.auto]]:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

5

  1. Insert new paragraphs before 13.8.3.2 [] paragraph 7 and change the latter as follows: An initializer is dependent if any constituent expression [6.9.1 [intro.execution]] of the initializer is type-dependent. A placeholder type [9.2.9.6.1 [dcl.spec.auto.general]] is dependent if it designates a type deduced from a dependent initializer.

A placeholder for a deduced class type [9.2.9.7 [dcl.type.class.deduct]] is dependent if

it has a dependent initializer, or it refers to an alias template that is a member of the current instantiation and whose defining-type-id is dependent after class template argument deduction [12.2.2.9 [over.match.class.deduct]] and substitution [13.7.8 [temp.alias]].

[ Example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

7

-- end example ]

A type is dependent if it is

... a function type whose exception specification is value-dependent, denoted by a dependent placeholder type, denoted by a dependent placeholder for a deduced class type,
  • ...

Issues with "Review" Status

2700.

error disallows existing implementation practice

Section: 4.1.1 [intro.compliance.general] Status: review Submitter: Richard Smith Date: 2023-02-13 Liaison: WG14

The resolution for disallows existing implementation practice, as detailed below:

  • The requirement to issue a diagnostic message for each appearance of

    warning conflicts with existing implementations having a diagnostic limit. Furthermore, an implementation with an on-the-fly incremental preprocessor is no longer allowed to abort the compilation when an error deemed fatal happens [e.g. a

    include file that does not exist].

  • The wording does not allow for implementation-specific mechanisms for creating a preprocessing translation unit, such as vendor-specific forms of

    if-like directives and

    pragma once.

Suggested resolution [SUPERSEDED]:

  1. Change in 4.1.1 [] paragraph 2 as follows:
    Furthermore, a conforming implementation shall not accept a preprocessing translation unit containing a

    error preprocessing directive [15.8 [cpp.error]], and

    shall issue at least one diagnostic message for each

    warning or

    error preprocessing directive not following a

    error preprocessing directive in a preprocessing translation unit, and

  2. shall not accept a translation unit with a static_assert-declaration that fails [9.1 [dcl.pre]].
  3. Change in 5.1 [] paragraph 1 as follows:

    The text of the program is kept in units called source files in this document. A source file together with all the headers [16.4.2.3 [headers]] and source files included [15.3 [cpp.include]] via the preprocessing directive

    include, less any source lines skipped by any of the conditional inclusion [15.2 [cpp.cond]] preprocessing directives or by the implementation-defined behavior of any conditionally-supported-directives [15.1 [cpp.pre]], is called a preprocessing translation unit.

CWG 2023-03-03

Permit that

warning can be ignored if another diagnostic is produced.

Proposed resolution:

  1. Change in 4.1.1 [] bullet 2.3 as follows:
    ... Otherwise, if a program contains a violation of any diagnosable rule or, a preprocessing translation unit with a

    warning preprocessing directive [15.8 [cpp.error]], or

    an occurrence of a construct described in this document as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.
  2. Change in 4.1.1 [] paragraph 2 as follows:

    Furthermore, a conforming implementation shall not accept

    a preprocessing translation unit containing a

    error preprocessing directive [15.8 [cpp.error]], or

    shall issue at least one diagnostic message for each

    warning or

    error preprocessing directive not following a

    error preprocessing directive in a preprocessing translation unit, and

    shall not accept a translation unit with a static_assert-declaration that fails [9.1 [dcl.pre]].
  3. Change in 5.1 [] paragraph 1 as follows:

    The text of the program is kept in units called source files in this document. A source file together with all the headers [16.4.2.3 [headers]] and source files included [15.3 [cpp.include]] via the preprocessing directive

    include, less any source lines skipped by any of the conditional inclusion [15.2 [cpp.cond]] preprocessing directives, as modified by the implementation-defined behavior of any conditionally-supported-directives [15.1 [cpp.pre]] and pragmas [15.9 [cpp.pragma]], if any, is called a preprocessing translation unit.

  4. Change in 15.8 [cpp.error] as follows: A preprocessing directive of either of the following forms the form

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

8 renders the program ill-formed. A preprocessing directive of the form

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

9 causes requires the implementation to produce a at least one diagnostic message for the preprocessing translation unit [4.1.1 [intro.compliance.general]] that.

Recommended practice: Any diagnostic message caused by either of these directives should include the specified sequence of preprocessing tokens; the

error directive renders the program ill-formed.

2726. Alternative tokens appearing as attribute-tokens

Section: 5.5 [lex.digraph] Status: review Submitter: Jim X Date: 2023-03-16 Liaison: EWG

Subclause 5.5 [] paragraph 2 specifies:

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

However, 9.12.1 [] paragraph 4 specifies:

... If a keyword [5.11 [lex.key]] or an alternative token [5.5 [lex.digraph]] that satisfies the syntactic requirements of an identifier [5.10 [lex.name]] is contained in an attribute-token, it is considered an identifier. ...

It seems an alternative token [say, or] is treated the same as the operator ||, yet only the alternative token is considered an identifier, not the operator, when appearing in an attribute-token. That seems contradictory.

Proposed resolution [approved by CWG 2023-05-12]:

  1. Change in 5.2 [] paragraph 2 as follows:
    Whitespace characters separating tokens are no longer significant. Each preprocessing token is converted into a token [5.6 [lex.token]], replacing each alternative token by its corresponding primary token [5.5 [lex.digraph]]. The resulting tokens constitute a translation unit and are syntactically and semantically analyzed and translated.
  2. Change in 9.12.1 [] paragraph 4 as follows: ... If a keyword [5.11 [lex.key]] or an alternative token [5.5 [lex.digraph]] that satisfies the syntactic requirements of an identifier [5.10 [lex.name]] is contained in an attribute-token, it is considered an identifier. ...

This resolution also addresses .

Additional notes [May, 2023]

During additional discussion on the EWG reflector, Alisdair Meredith expressed that he intends to propose that some alternative tokens be treated as full keywords in phase 7, to prevent and from being used as an rvalue reference and compl from being used for naming destructors. That would reverse the above direction.

Furthermore, an idea was floated to treat all alternative tokens as full keywords in phase 7 and as identifiers in phase 4, amending the grammar productions for expressions as necessary. This removes the special treatment of alternative tokens entirely, however the treatment of examples such as

define and blah would change from ill-formed to well-formed. Some opposition was voiced against changing the phase 4 treatment of alternative tokens.

CWG 2023-06-16

Forwarded to EWG with paper issue

1581.

1924. Definition of “literal” and kinds of literals

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

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

1897. ODR vs alternative tokens

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

According to 5.5 [] 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 6.3 [] 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.

CWG 2023-05-12

Addressed by .

2514. Modifying const subobjects

Section: 6.7.3 [basic.life] Status: review Submitter: Jiang An Date: 2021-11-07 Liaison: SG12

The change in C++20 for allows transparently replacing a const subobject whose complete object is not const, and the new object can be non-const. However, if the reuse of the object has not happened, modifying such subobjects is still undefined behavior.

This restriction causes problems in the implementation of std::map and std::unordered_map; see this bug report. Here, the key_type objects in map containers are const, and implementations generally can't replace these objects after construction.

Perhaps these restrictions can be relaxed to assist in this case: if

  • the complete object of a key_type subobject in a container is not const [as the mapped_type subobject is not const], or
  • the complete object has dynamic storage duration

a const subobject could be modified.

[Is it meaningful to allow a new-expression like new const int[42] to create cv-qualified objects? Perhaps such objects should be unqualified, while maintaining the cv-qualification in the type of the expression?]

Notes from the November, 2022 meeting

The advice of SG12 is solicited; see cplusplus/papers

1395.

2551. "Refers to allocated storage" has no meaning

Section: 6.7.3 [basic.life] Status: review Submitter: Andrey Erokhin Date: 2020-09-07

6.7.3 [] paragraph 6 specifies:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such a pointer refers to allocated storage [6.7.5.5.2 [basic.stc.dynamic.allocation]], and using the pointer as if the pointer were of type void* is well-defined.

Similarly, 6.7.3 [] paragraph 7 specifies:

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such a glvalue refers to allocated storage [6.7.5.5.2 [basic.stc.dynamic.allocation]], and using the properties of the glvalue that do not depend on its value is well-defined.

In either case, it is unclear what "refers to allocated storage" means, beyond the properties ascribed to an object in 6.7.2 [intro.object].

See also .

Proposed resolution:

  1. Change in 6.7.3 [] paragraph 6 as follows:
    For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such a pointer refers to allocated storage [6.7.5.5.2 [basic.stc.dynamic.allocation]], and using the such a pointer as if the pointer were of type void* is well-defined.
  2. Change in 6.7.3 [] paragraph 7 as follows: For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such a glvalue refers to allocated storage [6.7.5.5.2 [basic.stc.dynamic.allocation]], and using the properties of the such a glvalue that do not depend on its value is well-defined.

2677. Replacing union subobjects

Section: 6.7.3 [basic.life] Status: review Submitter: Richard Smith Date: 2022-12-06

The resolution to NB comment US 041 [C++20 CD] does not seem to have fully addressed the original issue, allowing:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

0

The suggestion is to allow a newly-created complete object to transparently replace any object of the same type in the same storage, except for a potentially-overlapping subobject or a const complete object, and to allow a newly-created subobject to transparently replace only a corresponding subobject of an existing object.

Suggested resolution [SUPERSEDED]:

Change in 6.7.3 [] paragraph 8 as follows:

... An object o1 is transparently replaceable by an object o2 if: o2 is a complete object and: the storage that o2 occupies exactly overlays the storage that o1 occupied, and o1 and o2 are of the same type [ignoring the top-level cv-qualifiers], and o1 is not a const, complete object, and neither o1 nor o2 is not a potentially-overlapping subobject [6.7.2 [intro.object]], and or either o1 and o2 are both complete objects, or o1 and o2 are corresponding direct subobjects of objects p1 and p2, respectively, and p1 is transparently replaceable by p2.

Additional notes [February, 2023]

The suggested resolution above does not handle the additional example in .

Suggested resolution:

  1. Change in 6.7.2 [] paragraph 2 as follows:
    Objects can contain other objects, called subobjects. A subobject can be a member subobject [11.4 [class.mem]], a base class subobject [11.7 [class.derived]], or an array element. An object that is not a subobject of any other object is called a complete object. If an object is created in storage associated with a member subobject or array element subobject e [which may or may not be within its lifetime], the created object is a subobject of e's containing object if: the lifetime of e's containing object has begun and not ended, and the storage for the new object exactly overlays the storage location associated with e, and e is not a potentially-overlapping subobject, and the new object is of the same type as e [ignoring cv-qualification]. In this case, e and the created object are corresponding direct subobjects.
  2. Change in 6.7.3 [] paragraph 8 as follows:

    ... An object o1 is transparently replaceable by an object o2 if either

    o1 and o2 are complete objects for which: o1 is not const, the storage that o2 occupies exactly overlays the storage that o1 occupied, and o1 and o2 are of the same type [ignoring the top-level cv-qualifiers], and or o1 is not a const, complete object, and neither o1 nor o2 is a potentially-overlapping subobject [6.7.2 [intro.object]], and either o1 and o2 are both complete objects, or o1 and o2 are corresponding direct subobjects of objects p1 and p2, respectively, and p1 is transparently replaceable by p2 [6.7.2 [intro.object]] for which: the complete object of o1 is not const or
  3. o1 is a mutable member subobject or a subobject thereof.

2533. Storage duration of implicitly created objects

Section: 6.7.5 [basic.stc] Status: review Submitter: Andrey Erokhin Date: 2022-02-17

In subclause 6.7.2 [] paragraph 10, operations implicitly creating objects are defined:

Some operations are described as implicitly creating objects within a specified region of storage. For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types [6.8.1 [basic.types.general]] in its specified region of storage if...

However, the standard does not specify the storage duration that such an implicitly-created object has; this new method of object creation is not mentioned in 6.7.5.1 [] paragraph 2:

Static, thread, and automatic storage durations are associated with objects introduced by declarations [6.2 [basic.def]] and implicitly created by the implementation [6.7.7 [class.temporary]]. The dynamic storage duration is associated with objects created by a new-expression [7.6.2.8 [expr.new]].

With the exception of malloc, the storage duration should probably be that of the object providing storage [if any], similar to the provision for subobjects in 6.7.5.6 [basic.stc.inherit]:

The storage duration of subobjects and reference members is that of their complete object [6.7.2 [intro.object]].

The storage duration of an object created by a non-allocating form of an allocation function [17.6.3.4 [new.delete.placement]] should be treated similarly.

Possible resolution:

  1. Change in 6.7.2 [] paragraph 13 as follows:
    Any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects with dynamic storage duration in the returned region of storage and returns a pointer to a suitable created object.
  2. Change in 6.7.5.1 [] paragraph 2 as follows: Static, thread, and automatic storage durations are associated with objects introduced by declarations [6.2 [basic.def]] and implicitly created by the implementation [6.7.7 [class.temporary]]. The dynamic storage duration is associated with objects created by a new-expression [7.6.2.8 [expr.new]] in storage returned by an allocation function [6.7.5.5.2 [basic.stc.dynamic.allocation]] other than a non-allocating form [17.6.3.4 [new.delete.placement]] or by C library memory allocation [20.2.12 [c.malloc]].
  3. Change in 6.7.5.5.2 [] paragraph 3 as follows: For an allocation function other than a reserved placement allocation function other than a non-allocating form [17.6.3.4 [new.delete.placement]], the pointer returned on a successful call shall represent the address of storage that is aligned as follows:
  4. Change in 6.7.5.6 [] paragraph 1 as follows: The storage duration of subobjects and reference members is that of their complete object. The storage duration of an object nested within another object x is the storage duration of x [6.7.2 [intro.object]].
  5. Change in 7.6.2.8 [] paragraph 9 as follows: An object created by a new-expression that invokes an allocation function with a non-allocating form [see below] has the storage duration of the object that used to occupy the region of storage where the new object is created. Objects Any other object created by a new-expression have has dynamic storage duration [6.7.5.5 [basic.stc.dynamic]]. [Note 5: The lifetime of such an object is not necessarily restricted to the scope in which it is created. —end note]
  6. Change in 20.2.12 [] paragraph 4 as follows: These functions implicitly create objects [6.7.2 [intro.object]] with dynamic storage duration in the returned region of storage and return a pointer to a suitable created object. In the case of calloc and realloc, the objects are created before the storage is zeroed or copied, respectively.

2689. Are cv-qualified std::nullptr_t fundamental types?

Section: 6.8.2 [basic.fundamental] Status: review Submitter: Anoop Rana Date: 2022-12-08

It is unclear whether cv std::nullptr_t is a fundamental type, given that it is declared in a library header and cv-qualifications are not mentioned in 6.8.2 [] paragraph 15.

Proposed resolution:

Change in 6.8.2 [] paragraph 15 as follows:

The types denoted by std::nullptr_t, const std::nullptr_t, volatile std::nullptr_t, and const volatile std::nullptr_t are distinct types. A value of type std::nullptr_t is a null pointer constant [7.3.12 [conv.ptr]]. Such values participate in the pointer and the pointer-to-member conversions [7.3.12 [conv.ptr], 7.3.13 [conv.mem]]. sizeof[std::nullptr_t] shall be equal to sizeof[void*]. The types described in this subclause are called fundamental types. [Note 11: Even if the implementation defines two or more fundamental types to have the same value representation, they are nevertheless different types. —end note]

2587. Visible side effects and initial value of an object

Section: 6.9.2.2 [intro.races] Status: review Submitter: Andrey Erokhin Date: 2022-05-10

Subclause 6.9.2.2 [] paragraph 13 specifies:

A visible side effect A on a scalar object or bit-field M with respect to a value computation B of M satisfies the conditions: A happens before B and there is no other side effect X to M such that A happens before X and X happens before B. The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A.

However, a side effect is defined as 6.9.1 [] paragraph 7:

Reading an object designated by a volatile glvalue [7.2.1 [basic.lval]], modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

It seems that initialization of an object is not a side effect, and thus the value of an scalar object can never be the value obtained during initialization.

Proposed resolution:

Change in 6.9.1 [] paragraph 7 as follows:

Reading an object designated by a volatile glvalue [7.2.1 [basic.lval]], modifying an object [including initialization], calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. ...

2738. "denotes a destructor" is missing specification

Section: 7.5.4.2 [expr.prim.id.unqual] Status: review Submitter: Jim X Date: 2022-05-22

Subclause 7.5.4.2 [] paragraph 1 has a note that lacks a corresponding normative specification:

[Note 1: For operator-function-ids, see 12.4 [over.oper]; for conversion-function-ids, see 11.4.8.3 [class.conv.fct]; for literal-operator-ids, see 12.6 [over.literal]; for template-ids, see 13.3 [temp.names]. A type-name or decltype-specifier prefixed by ~ denotes the destructor of the type so named; see 7.5.4.4 [expr.prim.id.dtor]. Within the definition of a non-static member function, an identifier that names a non-static member is transformed to a class member access expression [11.4.3 [class.mfct.non.static]]. —end note]

Proposed resolution:

Change in 7.5.4.2 [] paragraph 1 as follows:

A type-name or decltype-specifier prefixed by ~ denotes the destructor of the named type; see 7.5.4.4 [expr.prim.id.dtor]. [Note 1: For operator-function-ids, see 12.4 [over.oper]; for conversion-function-ids, see 11.4.8.3 [class.conv.fct]; for literal-operator-ids, see 12.6 [over.literal]; for template-ids, see 13.3 [temp.names]. A type-name or decltype-specifier prefixed by ~ denotes the destructor of the type so named; see 7.5.4.4 [expr.prim.id.dtor]. Within the definition of a non-static member function, an identifier that names a non-static member is transformed to a class member access expression [11.4.3 [class.mfct.non.static]]. —end note]

2549. Implicitly moving the operand of a throw-expression in unevaluated contexts

Section: 7.5.4.3 [expr.prim.id.qual] Status: review Submitter: Richard Smith Date: 2022-03-11

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

1

11.9.6 [] paragraph 3 specifies:

An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation is first considered before attempting a copy operation: ... if the operand of a throw-expression [7.6.18 [expr.throw]] is a [possibly parenthesized] id-expression that names an implicitly movable entity that belongs to a scope that does not contain the compound-statement of the innermost try-block or function-try-block [if any] whose compound-statement or ctor-initializer contains the throw-expression,

Thus, in the first example above, x is treated as an xvalue, but it is treated as an lvalue in the second example. This outcome is surprising.

[P2266R2 [Simpler implicit move] moved this wording, introduced by P1825R0 [Merged wording for P0527R1 and P1155R3], from 11.9.6 [class.copy.elision] to 7.5.4.3 [expr.prim.id.qual].]

Proposed resolution:

Change in 7.5.4.2 [] paragraph 4:

An implicitly movable entity is a variable of with automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following contexts, an An id-expression is move-eligible: if it names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression and If the id-expression [possibly parenthesized] id-expression is the operand of a return or co_return statement, and names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression or if the id-expression [possibly parenthesized] is the operand of a potentially-evaluated throw-expression , and names an implicitly movable entity that belongs to a scope that does not contain the compound-statement of the innermost lambda-expression, try-block, or function-try-block [if any] whose compound-statement or ctor-initializer encloses the throw-expression where no try-block or function-try-block intervenes between the declaration of the entity and the innermost enclosing scope of the throw-expression.

2561. Conversion to function pointer for lambda with explicit object parameter

Section: 7.5.5.2 [expr.prim.lambda.closure] Status: review Submitter: Barry Revzin Date: 2022-02-14

P0847R7 [Deducing this] [approved October, 2021] added explicit-object member functions. Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

2

Subclause 7.5.5.2 [] paragraph 8 does not address explicit object member functions:

The closure type for a non-generic lambda-expression with no lambda-capture whose constraints [if any] are satisfied has a conversion function to pointer to function with C++ language linkage [9.11 [dcl.link]] having the same parameter and return types as the closure type's function call operator. The conversion is to “pointer to noexcept function” if the function call operator has a non-throwing exception specification. The value returned by this conversion function is the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type. F is a constexpr function if...

Suggested resolution:

  1. Change in 7.5.5.2 [] paragraph 8 as follows:
    ... The value returned by this conversion function is for a lambda-expression whose parameter-declaration-clause has an explicit object parameter, the address of the function call operator [7.6.2.2 [expr.unary.op]; otherwise, the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type. F is a constexpr function if... is an immediate function.

[ Example:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char s = "x";

static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

3

-- end example ]

  1. Change in 7.5.5.2 [] paragraph 11 as follows:

    The value returned by any given specialization of this conversion function template is

    for a lambda-expression whose parameter-declaration-clause has an explicit object parameter, the address of the corresponding function call operator template specialization [7.6.2.2 [expr.unary.op]];
  2. otherwise, the address of a function F that, when invoked, has the same effect as invoking the generic lambda's corresponding function call operator template specialization on a default-constructed instance of the closure type. F is a constexpr function if...

2566. Matching deallocation for uncaught exception

Section: 7.6.2.8 [expr.new] Status: review Submitter: Jim X Date: 2022-04-13

Initialization of an object may terminate via an exception, in which case any dynamically-allocated memory is freed, per 7.6.2.8 [] paragraph 26:

If any part of the object initialization described above [ Footnote: ... ] terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed.

However, implementations do not consistently support this provision in case the exception remains uncaught:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

4

Both clang and GCC do not free the memory in this example; they do so if the exception is caught in main.

Maybe a similar provision as used for stack unwinding in 14.4 [] paragraph 9 is desirable:

If no matching handler is found, the function std::terminate is invoked; whether or not the stack is unwound before this invocation of std::terminate is implementation-defined [14.6.2 [except.terminate]].

Suggested resolution:

Integrate freeing dynamically-allocated memory with stack unwinding [14.3 [except.ctor]], since this is what implementations actually do.

Possible resolution:

  1. Change in 7.6.2.8 [] paragraph 26, 27, and 28 as follows:
    If any part of the object initialization described above [ Footnote: This can include evaluating a new-initializer and/or calling a constructor. ] terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed. [Note 13: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. —end note]

For purposes of stack unwinding [14.3 [except.ctor]], the matching deallocation function is determined as follows: If the new-expression does not begin with a unary :: operator and the allocated type is a class type T or an array thereof, a search is performed for the deallocation function's name in the scope of T. Otherwise, or if nothing is found, the deallocation function's name is looked up by searching for it in the global scope.

A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations [9.3.4.6 [dcl.fct]], all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called is the matching deallocation function; otherwise, no deallocation function will be called there is no matching deallocation function. If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function [7.6.2.9 [expr.delete]].

  1. Change in 14.3 [] paragraph 1 as follows: As control passes from the point where an exception is thrown to a handler, objects are destroyed and deallocation functions are invoked by a process, specified in this subclause, called stack unwinding.
  2. Change in 14.3 [] paragraph 5 as follows: [Note 4: If the object was allocated by a new-expression [7.6.2.8 [expr.new]], If the evaluation of a new-expression other than the invocation of the allocation function is terminated by an exception, the matching deallocation function [6.7.5.5.3 [basic.stc.dynamic.deallocation]], if any, is called [7.6.2.8 [expr.new]] to free the storage occupied by the object. —end note]

2593. Insufficient base class restriction for pointer-to-member expression

Section: 7.6.4 [expr.mptr.oper] Status: review Submitter: Hubert Tong Date: 2022-06-04

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

5

Subclause 7.6.4 [] paragraph 4 specifies:

Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.

In the example, the dynamic type of a is C, which does contain B::x, and the undefined behavior provision does not trigger. Thus the call to f is required to yield 42; however common implementations produce 13. The behavior for this case ought to be undefined.

Suggested resolution:

Change in 7.6.4 [] paragraph 4 as follows:

Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of E1 does not contain the member to which E2 refers, Where the type of E2 is "pointer to member of T", C is the [unique] class of which the member to which E2 refers is a direct member, and B is the object of type T that either is the result of E1 or is the uniquely so-typed base subobject thereof, if B is neither of type C nor a base class subobject of an object of type C, then the behavior is undefined.

2548. Array prvalues and additive operators

Section: 7.6.6 [expr.add] Status: review Submitter: Andrey Erokhin Date: 2022-03-08

Consider

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

6

This appears to be ill-formed given the current wording, because the operand is already a prvalue, thus 7.2.1 [] paragraph 6 does not apply and the array-to-pointer conversion is not applied:

Whenever a glvalue appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue [7.3.2 [conv.lval]], array-to-pointer [7.3.3 [conv.array]], or function-to-pointer [7.3.4 [conv.func]] standard conversions are applied to convert the expression to a prvalue.

This outcome might be an oversight in the resolution for .

See also clang issue 54016.

Proposed resolution:

Change in 7.6.6 [] paragraph 1 as follows:

The additive operators + and - group left-to-right. The usual arithmetic conversions [7.4 [expr.arith.conv]] are performed for operands of arithmetic or enumeration type. The array-to-pointer conversion [7.3.3 [conv.array]] is applied to an operand of array type.

CWG 2023-07-14

CWG is generally in favor of the proposed resolution, but the interaction with the surrounding text needs to be checked in more detail after a Working Draft reflecting the Varna straw polls has become available.

2763. Ignorability of [[noreturn]] during constant evaluation

Section: 7.7 [expr.const] Status: review Submitter: Jiang An Date: 2023-07-10

Subclause 9.12.10 [] paragraph 2 specifies:

If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined.

Undefineed behavior is, in general, detected during constant evaluation, thus requiring an implementation to actually support the noreturn attribute, such as in the following example:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

7

It might be desirable to treat the assume and noreturn attributes alike in that regard.

Proposed resolution [approved by CWG 2023-07-14] [SUPERSEDED]:

Split into a separate paragraph and change 7.7 [] paragraph 5 as follows:

It is unspecified whether E is a core constant expression if E satisfies the constraints of a core constant expression, but evaluation of E would evaluate an operation that has undefined behavior as specified in Clause 16 through Clause 33, an invocation of the va_start macro [17.13.2 [cstdarg.syn]], or a return statement in a function that was previously declared with the noreturn attribute [9.12.10 [dcl.attr.noreturn]], or a statement with an assumption [9.12.3 [dcl.attr.assume]] whose converted conditional-expression, if evaluated where the assumption appears, would not disqualify E from being a core constant expression and would not evaluate to true. [ Note: .... ]

CWG 2023-07-14

As an alternative, all of 9.12 [dcl.attr] could be added to the "library undefined behavior" bullet. However, CWG felt that a case-by-case consideration is warranted, given that assumptions set precedent in requiring special treatment.

Possible resolution:

Split into a separate paragraph and change 7.7 [] paragraph 5 as follows:

... an operation that would have undefined behavior as specified in Clause 4 [intro] through Clause 15 [cpp], excluding 9.12.3 [dcl.attr.assume] and 9.12.10 [dcl.attr.noreturn]; [ Footnote: ...] ...

It is unspecified whether E is a core constant expression if E satisfies the constraints of a core constant expression, but evaluation of E would evaluate

an operation that has undefined behavior as specified in Clause 16 through Clause 33, an invocation of the va_start macro [17.13.2 [cstdarg.syn]], or a return statement in a function that was previously declared with the noreturn attribute [9.12.10 [dcl.attr.noreturn]], or
  • a statement with an assumption [9.12.3 [dcl.attr.assume]] whose converted conditional-expression, if evaluated where the assumption appears, would not disqualify E from being a core constant expression and would not evaluate to true. [ Note: .... ]

2531. Static data members redeclared as constexpr

Section: 9.2.6 [dcl.constexpr] Status: review Submitter: Davis Herring Date: 2022-02-16

C++17 made constexpr static data members implicitly inline [9.2.6 [] paragraph 1]:

A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable [9.2.8 [dcl.inline]].

However, that makes the following well-formed C++14 program ill-formed, no diagnostic required, per 9.2.8 [] paragraph 5:

If a function or variable with external or module linkage is declared inline in one definition domain, an inline declaration of it shall be reachable from the end of every definition domain in which it is declared; no diagnostic is required.

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

8

Proposed resolution [approved by CWG 2023-02-07]:

Change 9.2.6 [] paragraph 1 as follows:

A function or static data member declared with the constexpr or consteval specifier on its first declaration is implicitly an inline function or variable [9.2.8 [dcl.inline]].

Drafting note: Functions must be declared constexpr on every declaration if on any, so this isn't a change for them.

2412. SFINAE vs undeduced placeholder type

Section: 9.2.9.6 [dcl.spec.auto] Status: review Submitter: Mike Miller Date: 2019-05-03

The status of the following example is not clear:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

9

The static_assert causes the evaluation of the default template argument decltype[foo[int{}]]. However, foo is not defined, leaving it with an undeduced placeholder return type. This situation could conceivably be handled in two different ways. According to 9.2.9.6 [] paragraph 9,

If the name of an entity with an undeduced placeholder type appears in an expression, the program is ill-formed.

This would thus appear to be an invalid expression resulting from substitution in the immediate context of the declaration and thus a substitution failure.

The other alternative would be to treat the presence of an undeduced placeholder type for a function template as satisfying the requirements of 13.9.2 [] paragraph 4,

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 or if the existence of the definition affects the semantics of the program.

and attempt to instantiate foo. That instantiation fails because the definition is not provided, which would then be an error outside the immediate context of the declaration and thus a hard error instead of substitution failure.

CWG 2022-11-10

There is no implementation divergence on the handling of this example.

Possible resolution:

Change in 9.2.9.6.1 [] paragraph 11 as follows:

If a variable or function with an undeduced placeholder type is named by an expression [6.3 [basic.def.odr]], the program is ill-formed. Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. [ Example: ...

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

0 -- end example ]

2252. Enumeration list-initialization from the same type

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

According to 9.4.5 [] bullet 3.8,

Otherwise, if T is an enumeration with a fixed underlying type [9.7.1 [dcl.enum]], the initializer-list has a single element v, and the initialization is direct-list-initialization, the object is initialized with the value T[v] [7.6.1.4 [expr.type.conv]]; if a narrowing conversion is required to convert v to the underlying type of T , the program is ill-formed.

This could be read as requiring that there be a conversion from v to the underlying type of T, leaving the status of an example like the following unclear:

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

1

Notes from the March, 2018 meeting:

CWG disagreed that the existing wording requires such a conversion, only that if such a conversion is possble, it must not narrow. A formulation along the lines of “if that initialization involves a narrowing conversion to the underlying type of T...” was suggested to clarify the intent. This will be handled editorially, and the issue will be left in "review" status until the change has been verified.

2547. Defaulted comparison operator function for non-classes

Section: 9.5.2 [dcl.fct.def.default] Status: review Submitter: Jim X Date: 2022-03-07

[See editorial issue 5337.]

Subclause 9.5.2 [] paragraph 1 specifies:

A function definition whose function-body is of the form = default ; is called an explicitly-defaulted definition. A function that is explicitly defaulted shall be a special member function or a comparison operator function [12.4.3 [over.binary]], and not have default arguments.

There seem to be no further restrictions on which comparison operator functions are allowed to be defaulted. For example,

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

2

Subclause 11.10.1 [] paragraph 1 applies only to comparison operator functions "for some class":

A defaulted comparison operator function [12.4.3 [over.binary]] for some class C shall be a non-template function that is a non-static const non-volatile member of C having one parameter of type const C& and either no ref-qualifier or the ref-qualifier &, or a friend of C having either two parameters of type const C& or two parameters of type C.

Proposed resolution:

  1. Change in 9.5.2 [] paragraph 1 as follows:
    A function definition whose function-body is of the form = default ; is called an explicitly-defaulted definition. A function that is explicitly defaulted shall be a special member function [11.4.4 [special]] or a comparison operator function [12.4.3 [over.binary], 11.10.1 [class.compare.default]], and not have default arguments [9.3.4.7 [dcl.fct.default]].
  2. Change in 11.10.1 [] paragraph 1 as follows:

    A defaulted comparison operator function [12.4.3 [over.binary]] for some class C shall be a non-template function that is

    a non-static const non-volatile member of some class C having one parameter of type const C& and either no ref-qualifier or the ref-qualifier &, or a friend of some class C having either two parameters of type const C& or two parameters of type C.
Such a comparison operator function is termed a comparison operator function for class C. A comparison operator function for class C that is defaulted on its first declaration ...

2733. Applying [[maybe_unused]] to a label

Section: 9.12.8 [dcl.attr.unused] Status: review Submitter: Barry Revzin Date: 2023-05-25 Liaison: EWG

Subclause 9.12.8 [] paragraph 2 specifies:

The attribute may be applied to the declaration of a class, a typedef-name, a variable [including a structured binding declaration], a non-static data member, a function, an enumeration, or an enumerator.

Absent from that list are labels, but both gcc and clang accept [[maybe_unused]] on a label, and behave accordingly.

Proposed resolution [approved by CWG 2023-07-14]

Change in 9.12.8 [dcl.attr.unused] as follows:

The attribute-token maybe_unused indicates that a name, label, or entity is possibly intentionally unused. No attribute-argument-clause shall be present.

The attribute may be applied to the declaration of a class, a typedef-name, a variable [including a structured binding declaration], a non-static data member, a function, an enumeration, or an enumerator, or to an identifier label [8.2 [stmt.label]].

A name or entity declared without the maybe_unused attribute can later be redeclared with the attribute and vice versa. An entity is considered marked after the first declaration that marks it.

Recommended practice: For an entity marked maybe_unused, implementations should not emit a warning that the entity or its structured bindings [if any] are used or unused. For a structured binding declaration not marked maybe_unused, implementations should not emit such a warning unless all of its structured bindings are unused. For a label to which maybe_unused is applied, implementations should not emit a warning that the label is used or unused.

[Example 1:

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

3 Implementations should not warn that b or x is unused, whether or not NDEBUG is defined. — end example]

CWG 2023-07-14

CWG has reviewed and approved the proposed resolution. However, this is a new [albeit small] feature, thus forwarding to EWG via paper issue 1585 for approval.

2554. Overriding virtual functions, also with explicit object parameters

Section: 11.7.3 [class.virtual] Status: review Submitter: Jens Maurer Date: 2021-12-10

Consider:

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

4

Subclause 11.7.3 [] paragraph 2 says:

If a virtual member function F is declared in a class B, and, in a class D derived [directly or indirectly] from B, a declaration of a member function G corresponds [6.4.1 [basic.scope.scope]] to a declaration of F, ignoring trailing requires-clauses, then G overrides [ Footnote: ... ] F .

Subclause 6.4.1 [] paragraph 4 defines "corresponds" as follows:

Two declarations correspond if they [re]introduce the same name, both declare constructors, or both declare destructors, unless ... each declares a function or function template, except when both declare functions with the same non-object-parameter-type-list, equivalent [13.7.7.2 [temp.over.link]] trailing requires-clauses [if any, except as specified in 13.7.5 [temp.friend]], and, if both are non-static members, they have corresponding object parameters, or both declare function templates with...

Subclause 6.4.1 [] paragraph 3 defines "corresponding object parameters" as follows:

Two non-static member functions have corresponding object parameters if: exactly one is an implicit object member function with no ref-qualifier and the types of their object parameters [9.3.4.6 [dcl.fct]], after removing top-level references, are the same, or their object parameters have the same type.

In the example, B::f has an object parameter of type B, but D::f has an object parameter of type D. Thus, the two functions do not correspond, and thus D::f does not override B::f. That is an unintended alteration of the status quo ante.

See also .

Proposed resolution:

  1. Change in 11.7.3 [] paragraph 2 as follows:
    If a virtual member function F is declared in a class B, and, in a class D derived [directly or indirectly] from B, a declaration of a member function G corresponds [6.4.1 [basic.scope.scope]] to a declaration of F as if declared in D [12.2.2.1 [over.match.funcs.general]], ignoring trailing requires-clauses, and, if G is an explicit object member function, ignoring object parameters, and, if G is an implicit object member function, F and G have the same ref-qualifier [or absence thereof], then G overrides [ Footnote: ... ] F .
  2. Remove 11.7.3 [] paragraph 7 as follows: The ref-qualifier , or lack thereof, of an overriding function shall be the same as that of the overridden function.

2504. Inheriting constructors from virtual base classes

Section: 11.9.4 [class.inhctor.init] Status: review Submitter: Hubert Tong Date: 2021-11-03

According to 11.9.4 [] paragraph 1,

When a constructor for type B is invoked to initialize an object of a different type D [that is, when the constructor was inherited [9.9 [namespace.udecl]]], initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the invocation of the inherited constructor. The complete initialization is considered to be a single function call; in particular, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the Dobject.

First, this assumes that the base class constructor will be invoked from the derived class constructor, which will not be true if the base is virtual and initialized by a more-derived constructor.

If the call to the virtual base constructor is omitted, the last sentence is unclear whether the initialization of the base class constructor's parameters by the inheriting constructor occurs or not. There is implementation divergence in the initialization of V's parameter in the following example:

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

5

CWG telecon 2022-09-23:

Inheriting constructors from a virtual base class ought to be ill-formed. Inform EWG accordingly.

Possible resolution [SUPERSEDED]:

  1. Change in 9.9 [] paragraph 3 as follows:
    ... If a using-declarator names a constructor, its nested-name-specifier shall name a direct non-virtual base class of the current class. If the immediate [class] scope is associated with a class template, it shall derive from the specified base class or have at least one dependent base class.
  2. Change the example in 11.9.4 [] paragraph 1 as follows: template concept C = requires [typename T::type x] {
    x + 1;  
    
    }; static_assert[!C]; 6
  3. Change the example in 11.9.4 [] paragraph 2 as follows: template concept C = requires [typename T::type x] {
    x + 1;  
    
    }; static_assert[!C]; 7

CWG telecon 2022-10-07:

Given that there are examples that discuss inheriting constructors from virtual base classes and given the existing normative wording, making it clear that NonTriv is not constructed, CWG felt that the implementation divergence is best addressed by amending the examples.

Possible resolution:

Add another example before 11.9.4 [] paragraph 2 as follows:

[ Example:

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

8

In this example, the V subobject of b is constructed using the defaulted default constructor. The mem-initializer naming the constructor inherited from V at

1 is not evaluated and thus no object of type NonTriv is constructed. -- end example ]

If the constructor was inherited from multiple base class subobjects of type B, the program is ill-formed.

Additional notes [October, 2022]

Possible resolution:

  1. Change in 11.9.4 [] paragraph 1 as follows:
    When a constructor for type B is invoked to initialize an object of a different type D [that is, when the constructor was inherited [9.9 [namespace.udecl]]], initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, if the base class subobject were to be initialized as part of the D object [11.9.3 [class.base.init]], except that the B subobject is initialized by the invocation of the inherited constructor. The invocation of the inherited constructor, including the evaluation of any arguments, is omitted if the B subobject is not to be initialized as part of the D object. The complete initialization is considered to be a single function call; in particular, unless omitted, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the Dobject.
  2. Add another example before 11.9.4 [] paragraph 2 as follows: [ Example:

template concept C = requires [typename T::type x] { x + 1; }; static_assert[!C];

9

-- end example ]

CWG telecon 2022-10-21:

This is an ABI break for implementations when transitioning to the C++17 model for inheriting constructors.

2568. Access checking during synthesis of defaulted comparison operator

Section: 11.10.1 [class.compare.default] Status: review Submitter: Nicolai Josuttis Date: 2022-04-11

Consider:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

0

Per 11.10.1 [] paragraph 6,

Let xi be an lvalue denoting the i-th element in the expanded list of subobjects for an object x [of length n], where xi is formed by a sequence of derived-to-base conversions [12.2.4.2 [over.best.ics]], class member access expressions [7.6.1.5 [expr.ref]], and array subscript expressions [7.6.1.2 [expr.sub]] applied to x.

The derived-to-base conversion for this loses the context of access to the protected Base::operator==, violating 11.8.5 [] paragraph 1. The example is rejected by implementations, but ought to work.

For this related example, there is implementation divergence:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

1

Is D::operator== deleted, because its defaulted definition violates the protected access rules? Is D::operator== not deleted, but synthesis fails on use because of the proctected access rules? Is the synthesis not in the immediate context, making the expression comparable[D{}] ill-formed?

CWG 2023-06-17

There is no implementation divergence; the first example is intended to be well-formed.

Possible resolution:

Change in 11.10.1 [] paragraph 1 as follows:

... Name lookups and access checks in the implicit definition [9.5.2 [dcl.fct.def.default]] of a comparison operator function are performed from a context equivalent to its function-body . A definition of a comparison operator as defaulted that appears in a class shall be the first declaration of that function.

2703. Three-way comparison requiring strong ordering for floating-point types, take 2

Section: 11.10.3 [class.spaceship] Status: review Submitter: Richard Smith Date: 2023-02-13

The resolution accepted for does not actually address the example in the issue, because overload resolution is never performed for expressions involved only built-in types.

Suggested resolution:

Change in 11.10.3 [] paragraph 1 as follows:

The synthesized three-way comparison of type R [17.11.2 [cmp.categories]] of glvalues a and b of the same type is defined as follows: If a b is usable [11.10.1 [class.compare.default]] and can be explicitly converted to R using static_cast, static_cast[a b]. Otherwise, if a b is usable or overload resolution for a b is performed and finds at least one viable candidate, the synthesized three-way comparison is not defined.
  • Otherwise, ...

2546. Defaulted secondary comparison operators defined as deleted

Section: 11.10.4 [class.compare.secondary] Status: review Submitter: Jim X Date: 2022-03-07

[See also editorial issues 5335 and 5336.]

Consider the example in 11.10.4 [] paragraph 3:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

2

While the comment may reflect the intent, it does not follow from the wording. 11.10.4 [] paragraph 2 specifies:

The operator function with parameters x and y is defined as deleted if overload resolution [12.2 [over.match]], as applied to x @ y, does not result in a usable candidate, or the candidate selected by overload resolution is not a rewritten candidate. Otherwise, the operator function yields x @ y. The defaulted operator function is not considered as a candidate in the overload resolution for the @ operator.

Overload resolution applied to x < y results in a usable candidate operator [12.2.1 [over.match.general]] and that candidate is a rewritten candidate [12.2.2.3 [] bullet 3.4], thus operator< in the above example is not deleted. However, its definition is ill-formed, because the rewrite [x y] < 0 is ill-formed [12.2.2.3 [] paragraph 8].

There is implementation divergence.

Subclause 11.10.3 [] paragraph 1 seems to prefer an ill-formed program for similar synthesized situations:

[Note 1: A synthesized three-way comparison is ill-formed if overload resolution finds usable candidates that do not otherwise meet the requirements implied by the defined expression. —end note]

Suggested resolution:

Change in 11.10.4 [] paragraph 2 as follows:

The operator function with parameters x and y is defined as deleted if a first overload resolution [12.2 [over.match]], as applied to x @ y, does not result in a usable candidate, or the selected candidate selected by overload resolution is not a rewritten candidate. , or a second overload resolution for the expression resulting from the interpretation of x @ y using the selected rewritten candidate [12.2.2.3 [over.match.oper]] does not result in a usable candidate, or
  • x @ y cannot be implicitly converted to bool.
in any overload resolution, the defaulted operator function is not considered as a candidate for the @ operator. Otherwise, the operator function yields x @ y. The defaulted operator function is not considered as a candidate in the overload resolution for the @ operator.

2572. Address of overloaded function with no target

Section: 12.3 [over.over] Status: review Submitter: Jason Merrill Date: 2022-04-26

Consider:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

3

Accoring to 12.3 [] paragraph 3 and 12.3 [] paragraph 5:

The specialization, if any, generated by template argument deduction [13.10.4 [temp.over], 13.10.3.3 [temp.deduct.funcaddr], 13.10.2 [temp.arg.explicit]] for each function template named is added to the set of selected functions considered.

[...]

Any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.

Major implementations reject the example as ambiguous, yet the wording specifies to unambiguously choose

2.

Suggested resolution [SUPERSEDED]:

Change in 12.3 [] paragraph 5 as follows:

Any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized better than the function template of F1. If there is no target, a function template is better than another if it is more constrained than the other; otherwise a function template is better than another if it is more specialized than the other according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.

Proposed resolution:

Change in 12.3 [] paragraph 5 as follows:

Any If there is a target, any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.

2617. Default template arguments for template members of non-template classes

Section: 13.2 [temp.param] Status: review Submitter: Mike Miller Date: 2022-08-22

Consider:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

4

There is implementation divergence in the treatment of this example. The relevant wording appears to be 13.2 [] paragraph 12:

A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template that appears outside of the member's class.

However, the example above deals with a member of an ordinary class, not a class template, but it is not clear why there should be a difference between a member template of a class template and a member template of a non-template class.

Alternatively, it is not clear why the example above should be treated differently from a non-member function template, e.g.,

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

5

which is explicitly permitted.

Proposed resolution:

Change in 13.2 [] paragraph 10 as follows:

... A default template-argument may be specified in a template declaration. A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template templated class C that appears outside of the member's class class-specifier of C. A default template-argument shall not be specified in a friend class template declaration. If a friend function template declaration D specifies a default template-argument, that declaration shall be a definition and there shall be no other declaration of the function template which is reachable from D or from which D is reachable.

2589. Context of access checks during constraint satisfaction checking

Section: 13.5.2.3 [temp.constr.atomic] Status: review Submitter: Jason Merrill Date: 2019-10-02

Consider:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

6

Should the context of instantiation be considered for satisfaction checking? If satisfaction checking were always performed in an unrelated context, neither partial specialization is used, and

1 would be ill-formed [because B is incomplete], but

2 would be well-formed. If the satisfaction checking were performed in the context of the constrained declaration,

1 would be well-formed and

2 would be ill-formed, no diagnostic required, because the validity of A[] is different in that context. That rule, however, could also consider the context, in which case

2 would also be well-formed.

The decision affects the amount of caching that an implementation can perform.

Subclause 13.5.2.3 [] paragraph 3 should be clarified one way or another:

To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise, the lvalue-to-rvalue conversion [7.3.2 [conv.lval]] is performed if necessary, and E shall be a constant expression of type bool. The constraint is satisfied if and only if evaluation of E results in true. If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.

Proposed resolution:

Change in 13.5.2.3 [] paragraph 3 as follows:

To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied; access checking is performed in the context in which the constraint-expression or requires-expression appears. Otherwise, the lvalue-to-rvalue conversion [7.3.2 [conv.lval]] is performed if necessary, and E shall be a constant expression of type bool. ...

1602. Linkage of specialization vs linkage of template arguments

Section: 13.9.2 [temp.inst] Status: review Submitter: Richard Smith Date: 2013-01-09

The Standard does not appear to specify the linkage of a template specialization. 13.9.2 [] 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.

Additional notes [February, 2023]

Template specializations do not have linkage.

2054. Missing description of class SFINAE

Section: 13.10.3 [temp.deduct] Status: review 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:

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

7

However, this does not appear to be described anywhere in the Standard.

Additional notes [January, 2023]

The section on SFINAE [13.10.3.1 [] paragraph 8] is not specific to function templates, and 13.7.6.2 [] paragraph 2 hands off the "matching" determination for partial specializations to 13.10.3 [temp.deduct] in general. However, the definition of deduction substitution loci in 13.10.3.1 [] paragraph 7 does not account for the template argument list of a partial specialization.

Possible resolution:

Change in 13.10.3.1 [] paragraph 7 as follows:

The deduction substitution loci are the function type outside of the noexcept-specifier, the explicit-specifier, and the template parameter declarations., and the template argument list of a partial specialization [13.7.6.1 [temp.spec.partial.general]]. The substitution occurs in all types and expressions that are used in the deduction substitution loci. ...

Issues with "Drafting" Status

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

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

5.4 [] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 5.12 [] 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

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

8

is a well-formed control-line, since that requires an identifier after the define token.

[See also .]

1655. Line endings in raw string literals

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

According to 5.4 [] paragraph 3,

If the input stream has been parsed into preprocessing tokens up to a given character:
  • If the next character begins a sequence of characters that could be the prefix and initial double quote of a raw string literal, such as R", the next preprocessing token shall be a raw string literal. Between the initial and final double quote characters of the raw string, any transformations performed in phases 1 and 2 [trigraphs, universal-character-names, and line splicing] are reverted; this reversion shall apply before any d-char, r-char, or delimiting parenthesis is identified.

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 [5.3 [lex.charset]] is replaced by the universal-character-name that designates that character.

The reversion described in 5.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: 5.6 [lex.token] Status: drafting Submitter: Richard Smith Date: 2014-03-25

The syntactic nonterminal punctuator appears in the grammar for token in 5.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.

Proposed resolution [October, 2017]:

  1. Change 5.5 [] paragraph 2 as follows
    In all respects of the language except in an attribute-token [9.12.1 [dcl.attr.grammar]], each alternative token behaves the same, respectively, as its primary token, except for its spelling.18 The set of alternative tokens...
  1. Change the grammar in 5.6 [lex.token] as follows:

    token: identifier keyword literal-token operator punctuator

    literal-token: integer-literal floating-literal character-literal string-literal user-defined-literal

    symbol: one of

struct A {

static void f[];
static void f[int];
} x; void [*p][] = x.f; // error

9

  1. Change 5.6 [] paragraph 1 as follows:
    There are five four kinds of tokens: identifiers, keywords, literals,19 operators, and other separators and symbols. Blanks, horizontal and vertical tabs, newlines, formfeeds, and comments [collectively, “white space”], as described below, are ignored except as they serve to separate tokens. [Note: Some white space is required to separate otherwise adjacent identifiers, keywords, numeric literals, and alternative tokens containing alphabetic characters. —end note] Each preprocessing-token resulting from translation phase 6 is converted into the corresponding token as follows:
If the preprocessing-token is an identifier or is one of the preprocessing-op-or-punc tokens new or delete, the resulting token is a keyword if it is listed in Table 5, and otherwise is an identifier. Otherwise, if the preprocessing-token is a pp-number with the lexical form of an integer-literal or floating-literal, or is a character-literal or string-literal, the resulting token is of the corresponding form. Otherwise, if the preprocessing-token is a pp-number with the lexical form of a user-defined-integer-literal or user-defined-floating-literal or is a user-defined-character-literal or user-defined-string-literal, the resulting token is a user-defined-literal. Otherwise, if the preprocessing-token is a preprocessing-op-or-punc, and there is a corresponding symbol [after converting alternative token representations to their primary tokens], the resulting token is that symbol.
  • Otherwise, the program is ill-formed.
[Note: Within an attribute-token [9.12.1 [dcl.attr.grammar]], a token formed from a preprocessing-token that satisfies the syntactic requirements of an identifier is considered to be an identifier with the spelling of the preprocessing-token. —end note]
  1. Delete the final sentence of 5.12 [] paragraph 1.
    Each preprocessing-op-or-punc is converted to a single token in translation phase 7 [5.2 [lex.phases]].

189. Definition of operator and punctuator

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

The nonterminals operator and punctuator in 5.6 [lex.token] are not defined. There is a definition of the nonterminal operator in 12.4 [] 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 5.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 12.4 [over.oper] to use the term overloadable-operator.
  2. Change 5.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 5.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 12.4 [] paragraph 3 among the operators that cannot be overloaded.

[See also .]

1723. Multicharacter user-defined character literals

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

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

According to 5.13.9 [] 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 [12.6 [over.literal]] whose only parameter has the type of ch and the literal L is treated as a call of the form operator "" X[ch]

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

operator "" _foo[int]

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

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

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

Section: 5.13.9 [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 5.13.9 [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: 6.1 [basic.pre] Status: drafting Submitter: Daniel Krügler Date: 2012-07-24

According to 6.1 [] 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.

2480. Lookup for enumerators in modules

Section: 6.5.1 [basic.lookup.general] Status: drafting Submitter: Richard Smith Date: 2021-02-12

According to 6.5.1 [basic.lookup.general] paragraphs 2-3,

...A declaration X precedes a program point P in a translation unit L if P follows X, X inhabits a class scope and is reachable from P, or else... A single search in a scope S for a name N from a program point P finds all declarations that precede P to which any name that is the same as N [6.1 [basic.pre]] is bound in S.

These rules cause problems for finding enumerators when qualified by an exported name of its enumeration type, unlike a member of a class. For example:

struct B {

static void f[];
} y; void [*q][] = y.f; // error

0

It would seem that this problem could be addressed by changing “inhabits a class scope” to “does not inhabit a namespace scope.”

1089. Template parameters in member selections

Section: 6.5.5.1 [basic.lookup.qual.general] Status: drafting Submitter: Daveed Vandevoorde Date: 2010-06-29

In an example like

struct B {

static void f[];
} y; void [*q][] = y.f; // error

1

The nested-name-specifier T:: looks like it refers to the template parameter. However, if this is instantiated with a type like

struct B {

static void f[];
} y; void [*q][] = y.f; // error

2

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

struct B {

static void f[];
} y; void [*q][] = y.f; // error

3

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.

Additional note [November, 2020]:

Paper P1787R6, adopted at the November, 2020 meeting, partially addresses this issue.

2324. Size of base class subobject

Section: 6.7.2 [intro.object] Status: drafting Submitter: GB Date: 2017-02-27

According to 6.7.2 [] paragraph 7,

Unless it is a bit-field [11.4.10 [class.bit]], a most derived object shall have a nonzero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size.

Base class objects of zero size is a misleading term, as sizeof such an object is non-zero. Size should not be a property of an object, rather of a type.

2325. std::launder and reuse of character buffers

Section: 6.7.2 [intro.object] Status: drafting Submitter: CA Date: 2017-02-27

The status of the following code should be explicitly indicated in the Standard to avoid surprise:

struct B {

static void f[];
} y; void [*q][] = y.f; // error

4

In particular, it appears that the call to std::launder has undefined behaviour because the captured copy of space is not established to provide storage for an object of type int [sub 6.7.2 [] paragraph 1]. Furthermore, the code has undefined behaviour also because it attempts to access the stored value of the int object through a glvalue of an array type other than one of the ones allowed by sub 7.2.1 [] paragraph 8.

2469. Implicit object creation vs constant expressions

Section: 6.7.2 [intro.object] Status: drafting Submitter: Hubert Tong Date: 2020-12-07

It is not intended that implicit object creation, as described in 6.7.2 [] paragraph 10, should occur during constant expression evaluation, but there is currently no wording prohibiting it.

Notes from the February, 2021 teleconference:

This issue was occasioned by , which is also the subject of LWG issue 3495. CWG reviewed the proposed resolution and agrees with it. The intended approach for this issue is to wait for LWG to resolve that issue, then add a note in the core section pointing out the implications of that requirement for implicit object creation.

1027. Type consistency and reallocation of scalar types

Section: 6.7.3 [basic.life] Status: drafting Submitter: Gabriel Dos Reis Date: 2010-02-03

Is the following well-formed?

struct B {

static void f[];
} y; void [*q][] = y.f; // error

5

The wording that is intended to prevent such shenanigans, 6.7.3 [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 storage for the new object exactly overlays the storage location which the original object occupied, and the new object is of the same type as the original object [ignoring the top-level cv-qualifiers], and...

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 and .]

Proposed resolution [August, 2010]:

This issue is resolved by the resolution of .

1530. Member access in out-of-lifetime objects

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

According to 6.7.3 [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 , 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 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 11.9.5 [] 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 B {

static void f[];
} y; void [*q][] = y.f; // error

6

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 B {

static void f[];
} y; void [*q][] = y.f; // error

7

1997. Placement new and previous initialization

Section: 6.7.4 [basic.indet] Status: drafting Submitter: Jason Merrill Date: 2014-09-08

Given the following example,

struct B {

static void f[];
} y; void [*q][] = y.f; // error

8

Should the preceding initializsation of the buffer carry over to the value of *ip? According to 6.7.4 [] paragraph 1,

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 [7.6.19 [expr.ass]].

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

1634. Temporary storage duration

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

According to 6.7.5 [] paragraph 2,

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

The apparent intent of the reference to 6.7.7 [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 6.7.7 [] paragraph 5.

There are two problems, however: first, the specification of lifetime extension of temporaries [also in 6.7.7 [] 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 6.7.3 [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 6.7.7 [class.temporary] and elsewhere that the storage in which a temporary is created is no longer available for reuse, as specified in 6.7.3 [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 issues and .

1676. auto return type for allocation and deallocation functions

Section: 6.7.5.5.2 [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 .]

2073. Allocating memory for exception objects

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

According to 6.7.5.5.2 [] paragraph 4,

[Note: In particular, a global allocation function is not called to allocate storage for objects with static storage duration [6.7.5.2 [basic.stc.static]], for objects or references with thread storage duration [6.7.5.3 [basic.stc.thread]], for objects of type std::type_info [7.6.1.8 [expr.typeid]], or for an exception object [14.2 [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 14.2 [] 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: 6.7.5.5.3 [basic.stc.dynamic.deallocation] Status: drafting Submitter: Richard Smith Date: 2014-11-13

According to 6.7.5.5.3 [] 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 14.5 [except.spec]: if a deallocation function throws an exception that is not allowed by its exception-specification, 14.5 [] paragraph 10 would appear to give the program defined behavior [calling std::unexpected[] or std::terminate[]]. [Note that 14.5 [] paragraph 18 explicitly allows an explicit exception-specification for a deallocation function.]

1211. Misaligned lvalues

Section: 6.7.6 [basic.align] Status: drafting Submitter: David Svoboda Date: 2010-10-20

6.7.6 [basic.align] speaks of “alignment requirements,” and 6.7.5.5.2 [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].

1701. Array vs sequence in object representation

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

According to 6.8 [] 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].

1986. odr-use and delayed initialization

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

The current wording of 6.9.3.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 [6.7.2 [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 B {

static void f[];
} y; void [*q][] = y.f; // error

9

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 6.9.3.2 [] paragraph 2. CWG affirmed that the correct relationship is “happens before” and not “sequenced before.”

2148. Thread storage duration and order of initialization

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

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

2444. Constant expressions in initialization odr-use

Section: 6.9.3.3 [basic.start.dynamic] Status: drafting Submitter: Davis Herring Date: 2019-11-06

According to 6.9.3.3 [] paragraph 3,

A non-initialization odr-use is an odr-use [6.3 [basic.def.odr]] not caused directly or indirectly by the initialization of a non-local static or thread storage duration variable.

Paragraphs 4-6 uses this term to exclude such odr-uses from consideration in determining the point by which a deferred initialization must be performed. A static_assert or a template argument expression can odr-use a variable, but it cannot be said to define any time during execution.

Suggestion: Add constant expression evaluation to the definition. Rename the term to “initializing odr-use” [based on effect rather than cause]. Add a note saying that no such odr-use can occur before main begins.

Notes from the February, 2021 teleconference:

CWG agreed with the direction.

2503. Unclear relationship among name, qualified name, and unqualified name

Section: 7.5.4 [expr.prim.id] Status: drafting Submitter: Jens Maurer Date: 2021-08-04

The phrases “name”, “qualified name” and “unqualified name” are used in various places. It is not clear that all names are either one or the other; there could, in fact, be a third kind of name that is neither.

See also editorial issue 4793.

2473. Parentheses in pseudo-destructor calls

Section: 7.5.4.4 [expr.prim.id.dtor] Status: drafting Submitter: Mike Miller Date: 2020-12-15

According to 7.5.4.4 [] paragraph 2,

If the id-expression names a pseudo-destructor, T shall be a scalar type and the id-expression shall appear as the right operand of a class member access [7.6.1.5 [expr.ref]] that forms the postfix-expression of a function call [7.6.1.3 [expr.call]].

This would appear to make the following example ill-formed, because it is the parenthesized expression and not the class member access that is the postfix-expression in the function call:

int f[]; auto [*fp][]=f;

0

Presumably this is an oversight.

2086. Reference odr-use vs implicit capture

Section: 7.5.5.3 [expr.prim.lambda.capture] 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, 7.5.5 [] 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: odr-uses [6.3 [basic.def.odr]] the entity, or names the entity in a potentially-evaluated expression [6.3 [basic.def.odr]] where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.

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

int f[]; auto [*fp][]=f;

1

1521. T{expr} with reference types

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

According to 7.6.1.4 [] 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 [9.4.5 [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.

2283. Missing complete type requirements

Section: 7.6.1.4 [expr.type.conv] Status: drafting Submitter: Richard Smith Date: 2016-06-27

P0135R1 [Wording for guaranteed copy elision through simplified value categories] removes complete type requirements from 7.6.1.3 [expr.call] [under the assumption that subclause 9.4 [dcl.init] has them; apparently it does not] and from 7.6.1.8 [] paragraph 3. These both appear to be bad changes and should presumably be reverted.

2557. Class member access referring to an unrelated class

Section: 7.6.1.5 [expr.ref] Status: drafting Submitter: Jens Maurer Date: 2022-03-25

Consider:

int f[]; auto [*fp][]=f;

2

There seems to be no requirement that the member named in a class member access actually is a member of the class of the object expression. Subclause 7.5.4.1 [] paragraph 3 does not cover static members:

An id-expression that denotes a non-static data member or non-static member function of a class can only be used: as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or ...

Suggested resolution:

  1. Change in 7.6.1.5 [] paragraph 4 as follows:
    Otherwise, the object expression shall be of class type. The class type shall be complete unless the class member access appears in the definition of that class. [Note: The program is ill-formed if the result differs from that when the class is complete [6.5.2 [class.member.lookup]]. —end note] [Note: 6.5.5 [basic.lookup.qual] describes how names are looked up after the . and -> operators. —end note] If E2 is a qualified-id, the terminal name of its nested-name-specifier shall denote the type of E1 or a base class thereof.

[Example:

int f[]; auto [fp][]=f;

3

—end example ]

  1. Change in 7.6.1.5 [] bullet 6.5 as follows: ... If E2 is a member an enumerator and the type of E2 is T, the expression E1.E2 is a prvalue. The type of E1.E2 is T. [Example:
      int f[];  
      auto [*fp][]=f;  
    
    4 —end example ]
  2. Change in 7.5.4.1 [] paragraph 3 as follows:

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

    as part of a class member access [7.6.1.5 [expr.ref]] in which the object expression refers to the member's class [ Footnote: ... ] or a class derived from that class, or to form a pointer to member [7.6.2.2 [expr.unary.op]], or if that id-expression denotes a non-static data member and it appears in an unevaluated operand.

1965. Explicit casts to reference types

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

The specification of dynamic_cast in 7.6.1.7 [] paragraph 2 [and const_cast in 7.6.1.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

int f[]; auto [*fp][]=f;

5

The behavior of static_cast is an odd hybrid:

int f[]; auto [*fp][]=f;

6

[Binding a const lvalue reference to an rvalue is permitted by 7.6.1.9 [] 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, 7.6.1.9 [] 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].

2243. Incorrect use of implicit conversion sequence

Section: 7.6.1.9 [expr.static.cast] Status: drafting Submitter: Hubert Tong Date: 2016-03-08

The term “implicit conversion sequence” is now used in some non-call contexts [e.g., 7.6.1.9 [] paragraph 4, 7.6.16 [] paragraph 4, 7.6.10 [] paragraph 4] ] and it is not clear that the current definition is suited for these additional uses. In particular, passing an argument in a function call is always copy-initialization, but some of these contexts require consideration of direct-initialization.

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 .

232. Is indirection through a null pointer undefined behavior?

Section: 7.6.2.2 [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: 6.9.1 [] paragraph 4 gives "dereferencing the null pointer" as an example of undefined behavior, and 9.3.4.3 [] paragraph 4 [in a note] uses this supposedly undefined behavior as justification for the nonexistence of "null references."

However, 7.6.2.2 [] 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: 7.6.1.8 [] 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 [7.3.12 [conv.ptr]], the typeid expression throws the bad_typeid exception [17.7.5 [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:

int f[]; auto [*fp][]=f;

7

Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:

int f[]; auto [*fp][]=f;

8

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 7.2.1 [] 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, 7.3.2 [conv.lval]:

An lvalue [7.2.1 [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 , 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 also resolves part of this issue.]

  1. Add the indicated words to 7.2.1 [] paragraph 2:
    An lvalue refers to an object or function or is an empty lvalue [7.6.2.2 [expr.unary.op]].
  2. Add the indicated words to 7.6.2.2 [] 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 [7.3.12 [conv.ptr]] or points one past the last element of an array object [7.6.6 [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 7.3.2 [conv.lval].—end note]
  3. Add the indicated words to 7.3.2 [] 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 [7.6.2.2 [expr.unary.op]], a program that necessitates this conversion has undefined behavior.
  4. Change 6.9.1 [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 . We added wording to 6.8.4 [] 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 . 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 .]

901. Deleted operator delete

Section: 7.6.2.8 [expr.new] Status: drafting Submitter: John Spicer Date: 20 May, 2009

It is not clear from 7.6.2.8 [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 6.3 [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.

Additional notes [April, 2023]:

An additional use-case for a deleted deallocation function would be to ensure that the initialization of the object is not potentially-throwing.

For cases where the deallocation function is never called from the constructor, access checking for it should not be done.

2281. Consistency of aligned operator delete replacement

Section: 7.6.2.8 [expr.new] Status: drafting Submitter: Richard Smith Date: 2016-06-27

We should require that a program that replaces the aligned form of operator delete also replaces the sized+aligned form. We only allow a program to replace the non-sized form without replacing the sized form for backwards compatibility. This is not needed for the alignment feature, which is new.

Notes from the March, 2018 meeting:

CWG concurred with the recommendation.

2623. Invoking destroying operator delete for constructor failure

Section: 7.6.2.8 [expr.new] Status: drafting Submitter: Blacktea Hamburger Date: 2022-08-25

Subclause 7.6.2.8 [] paragraph 28 specifies the lookup for the deallocation function that is invoked when the construction of the object in a new-expression exits via an exception. However, a destroying operator delete [6.7.5.5.3 [basic.stc.dynamic.deallocation]] should never be used, because the object in question has not yet been fully created.

Suggested resolution [SUPERSEDED]:

Change in 7.6.2.8 [] paragraph 28 as follows:

A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations [9.3.4.6 [dcl.fct]], all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called. If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function [7.6.2.9 [expr.delete]] , except that any destroying operator delete [6.7.5.5.3 [basic.stc.dynamic.deallocation]] is ignored.

2013. Pointer subtraction in large array

Section: 7.6.6 [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 f[]; auto [*fp][]=f;

9

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: 7.6.6 [expr.add] Status: drafting Submitter: Jonathan Wakely Date: 2015-10-20

The current direction for [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 . The major problem is when the elements of the vector contain constant or reference members; 6.7.3 [] 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.

2023. Composite reference result type of conditional operator

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

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

See also .

2316. Simplifying class conversions in conditional expressions

Section: 7.6.16 [expr.cond] Status: drafting Submitter: S. B. Tam Date: 2016-08-16

According to 7.6.16 [] paragraph 4,

Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows: ... If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has [possibly cv-qualified] class type: if T1 and T2 are the same class type [ignoring cv-qualification] and T2 is at least as cv-qualified as T1, the target type is T2, otherwise, if T2 is a base class of T1, the target type is cv1 T2, where cv1 denotes the cv-qualifiers of T1,
  • otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue [7.3.2 [conv.lval]], array-to-pointer [7.3.3 [conv.array]], and function-to-pointer [7.3.4 [conv.func]] standard conversions.

It seems that to satisfy the conditions in the first two sub-bullets, T2 must be a class type, in which case T2 is the same as the type described in the third sub-bullet, since the lvalue-to-rvalue conversion does not change types and the other two conversions do not apply to a class type. Thus, this bullet and sub-bullets could be simplified to:

* if E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has [possibly cv-qualified] class type, the target type is the type that E2 would have after applying the lvalue-to-rvalue [7.3.2 [conv.lval]], array-to-pointer [7.3.3 [conv.array]], and function-to-pointer [7.3.4 [conv.func]] standard conversions.

Notes from the August, 2020 teleconference:

This issue and suggested resolution predate the resolution of , which added the second sub-bullet [the citation above reflects the wording after adoption of ], giving the result the cv-qualification of T1 instead of that of T2. The suggested resolution would revert that accepted resolution.

1542. Compound assignment of braced-init-list

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

The specification of 7.6.19 [] 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

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
0

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,

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
1

would presumably always be a narrowing error, because after integral promotions, the type of c+1 is int. The similar 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.

See also .

1255. Definition problems with constexpr functions

Section: 7.7 [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:

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
2

CWG 2023-06-15

Definitions of member functions need an "as-needed" treatment. See issues and .

2166. Unclear meaning of “undefined constexpr function”

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

According to 7.7 [] bullet 2.3, an expression is a constant expression unless [among other reasons] it would evaluate

  • an invocation of an undefined constexpr function or an undefined constexpr constructor;

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: 7.7 [expr.const] Status: drafting Submitter: Hubert Tong Date: 2015-10-24

Similar to the concern of , the requirement of 7.7 [] bullet 2.7.1 for

  • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

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.

1680. Including for range-based for

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

A simple example like

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
3

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

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
4

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: 8.7 [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: 8.9 [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” [9.3.4.6 [] paragraph 2] is a semantic, not a syntactic, restriction, it does not influence disambiguation, which is “purely syntactic” [8.9 [] paragraph 3]. Consequently, some previously unambiguous expressions are now ambiguous. For example:

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
5

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.

CWG 2023-05-12

Both 8.9 [stmt.ambig] and 9.3.3 [dcl.ambig.res] need to be adjusted.

CWG 2023-06-13

Addressed by paper P2915R0.

2117. Explicit specializations and constexpr function templates

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

According to 9.2.6 [] 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: 9.2.9.6 [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: 9.2.9.6 [dcl.spec.auto] Status: drafting Submitter: Richard Smith Date: 2013-04-26

The current wording allows something like

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
6

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.

Additional note [December, 2021]:

See duplicate issue for additional details.

1868. Meaning of “placeholder type”

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

9.2.9 [] 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.

1488. abstract-pack-declarators in type-ids

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

The grammar for type-id in 11.3 [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

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
7

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

453. References may only bind to “valid” objects

Section: 9.3.4.3 [dcl.ref] Status: drafting Submitter: Gennaro Prota Date: 18 Jan 2004

9.3.4.3 [] 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:

auto [&ar][2] = L"a";  // Array bound declared, element type deduced
8

Suggested resolution: Changing the final part of 9.3.4.3 [] paragraph 4 to:

A reference shall be initialized to refer to an object or function. From its point of declaration on [see 6.4.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 [6.7.3 [basic.life], paragraph 6] [Note: On the other hand, a declaration like this:

auto [&ar][2] = L"a"; // Array bound declared, element type deduced

9 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 .]

Change 9.3.4.3 [] 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 [9.4.4 [dcl.init.ref]], nor a region of memory of suitable size and alignment to contain an object of the reference's type [6.7.2 [intro.object], 6.7.3 [basic.life], 6.8 [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 11.4.10 [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:

std::vector v;

0 —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 [] and also suggested referring to “storage” instead of “memory” [because 6.7.2 [intro.object] defines an object as a “region of storage”].

Proposed Resolution [April, 2005]:

[Note: the following wording depends on the proposed resolution for .]

Change 9.3.4.3 [] 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 [9.4.4 [dcl.init.ref]], nor a region of storage of suitable size and alignment to contain an object of the reference's type [6.7.2 [intro.object], 6.7.3 [basic.life], 6.8 [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 11.4.10 [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:

std::vector v;

1 —end example]

Note [February, 2006]:

The word “use” in the last paragraph of the proposed resolution was intended to refer to the description in 6.3 [] 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 6.3 [] paragraph 2 to apply to references.

Additional note [May, 2008]:

The proposed resolution for 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 6.8.4 [basic.compound]] and thus is not suuitable for the lvalue-to-rvalue conversion.

1001. Parameter type adjustment in dependent parameter types

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

According to 9.3.4.6 [] 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:

std::vector v;
2

If the const in

1 is dropped, f 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:

std::vector v;
3

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

[See also .]

1668. Parameter type determination still not clear enough

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

According to 9.3.4.6 [] 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

std::vector v;
4

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 .

2537. Overbroad grammar for parameter-declaration

Section: 9.3.4.6 [dcl.fct] Status: drafting Submitter: Davis Herring Date: 2021-02-25

9.3.4.6 [] paragraph 3 specifies the grammar for parameter-declaration:

std::vector v;
5

This is overly permissive; using a defining-type-specifier-seq instead of a decl-specifier-seq is sufficient.

Proposed resolution [November, 2022]:

  1. Change in 9.2.2 [] paragraph 4 as follows:
    There can be no static function declarations within a block, nor any static function parameters.
  2. Change in 9.2.2 [] paragraph 5 as follows: The extern specifier shall not be used in the declaration of a class member or function parameter.
  3. Change in 9.2.4 [] paragraph 1 as follows: The typedef specifier shall not be combined in a decl-specifier-seq with any other kind of specifier except a defining-type-specifier, and it shall not be used in the decl-specifier-seq of a parameter-declaration [9.3.4.6 [dcl.fct]] nor in the decl-specifier-seq of a function-definition [9.5 [dcl.fct.def]].
  4. Change in 9.2.8 [] paragraph 4 as follows: The inline specifier shall not appear on a block scope declaration or on the declaration of a function parameter.
  5. Change in 9.3.4.6 [] paragraph 3 as follows:
    std::vector v;  
    
    6

CWG 2023-02-07

Additional drafting is needed to address references to decl-specifier-seq in other parts of the standard. A list is . Furthermore, reducing the grammar to a type-specifier-seq appears to be sufficient.

325. When are default arguments parsed?

Section: 9.3.4.7 [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.

9.3.4.7 [] 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

std::vector v;
7 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 6.4.7 [] 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 6.4.7 [] 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?

std::vector v;
8 If that is well formed, at what point does the non-sensicalness of

std::vector v;
9 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,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

00 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 ' 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 7.3.6 [conv.qual]. According to 9.4.4 [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 12.2.4.2.5 [] 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. [12.2.4.2.5 [] paragraph 5 and 12.2.4.3 [] 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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

11

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?

  • References behave fundamentally different from pointers in combination with user defined conversions, although there is no reason to have this different treatment.
  • This difference only shows up in combination with user defined conversion sequences, for all other cases, there are special rules, e.g. 12.2.4.3 [] paragraph 3 rule 3.

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 9.4.4 [] paragraph 6, 7.3.6 [conv.qual] and probably 12.2.4.3 [] paragraph 3.

Another fix could be to add a special case in 12.2.4 [] paragraph 1.

CWG 2023-06-13

It was noted that the second example is not ambiguous, because a derived-to-base conversion is compared against an identity conversion. However, 12.2.4.2.5 [] paragraph 1 needs a wording fix so that it applies to conversion functions as well. CWG opined that the first example be made valid, by adding a missing tie-breaker for the conversion function case.

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

Section: 9.4.4 [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

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

12

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 12.2.4.3 [] paragraph 3 is not clear:

  • Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
    • ...
    • S1 and S2 are reference bindings [9.4.4 [dcl.init.ref]] and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

13

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 12.2.4.3 [] 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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

14

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 12.2.4.3 [over.ics.rank] bullet 3.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: Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if ... S1 and S2 are reference bindings [9.4.4 [dcl.init.ref]] and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference... or, if not that, S1 and S2 are reference bindings [9.4.4 [dcl.init.ref]] and S1 binds an lvalue reference to a function lvalue and S2 binds an rvalue reference to a function lvalue.

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

1827. Reference binding with ambiguous conversions

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

In the following case,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

15

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: 9.4.4 [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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

16

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 .

1996. Reference list-initialization ignores conversion functions

Section: 9.4.5 [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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

17

2144. Function/variable declaration ambiguity

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

The following fragment,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

18

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

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

19

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

1854. Disallowing use of implicitly-deleted functions

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

The resolution of 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.

2563. Initialization of coroutine result object

Section: 9.5.4 [dcl.fct.def.coroutine] Status: drafting Submitter: Tomasz Kamiński Date: 2022-04-06 Liaison: EWG

Subclause 9.5.4 [] paragraph 7 specifies:

The expression promise.get_return_object[] is used to initialize the returned reference or prvalue result object of a call to a coroutine. The call to get_return_object is sequenced before the call to initial-suspend and is invoked at most once.

It is unclear:

  • whether get_return_object[] is invoked inside or outside of the try-block shown in paragraph 5 [see ],
  • whether the prvalue result object may be initialized later [e.g. before the first actual suspension], and
  • if the initialization does occur later, by what mechanism the prvalue result of get_return_object is forwarded to that initialization.

There is implementation divergence.

Note that a user-defined conversion may be involved in the initialization of the coroutine's prvalue result object from get_return_object[]. Note also that the return type of get_return_object might be non-copyable and non-movable. However, there are certain programming patterns that would benefit from a late-initialized return value.

See also compiler explorer.

Suggested resolution [SUPERSEDED]:

Change in 9.5.4 [] paragraph 7 as follows:

The expression promise.get_return_object[] is used to initialize the The returned reference or prvalue result object of a call to a coroutine is copy-initialized with promise.get_return_object[]. The call to get_return_object initialization is sequenced before the call to initial-suspend and is invoked at most once.

Additional notes [January, 2023]

See also clang bug report

56532.

Forwarded to EWG with paper issue 1414, by decision of the CWG chair.

EWG 2023-02-06

EWG agrees that get_return_object is invoked outside of the try-block and that, if a conversion is needed, the return value of get_return_object is considered an xvalue that is later converted to the result object.

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

Section: 9.7.1 [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 9.7.1 [] 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 11.4 [] 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 13.7.2.6 [] paragraph 1:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

20

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

Notes from the October, 2012 meeting:

CWG agreed that an unscoped opaque enumeration in class scope should be forbidden.

2131. Ambiguity with opaque-enum-declaration

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

The declaration

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

21

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

[See also .]

2505. Nested unnamed namespace of inline unnamed namespace

Section: 9.8.2.2 [namespace.unnamed] Status: drafting Submitter: Nathan Sidwell Date: 2021-11-22

According to 9.8.2.2 [] paragraph 1,

An unnamed-namespace-definition behaves as if it were replaced by

inlineopt namespace unique { /* empty body */ } using namespace unique ; namespace unique { namespace-body }

where inline appears if and only if it appears in the unnamed-namespace-definition and all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the translation unit.

The use of a single identifier for all occurrences of unique within a translation unit leads to problems when an inline unnamed namespace contains a nested unnamed namespace, e.g.,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

22

In this case, the unnamed namespace cannot be reopened because the lookup for unique finds both the outer and inner namespaces and is thus ambiguous.

Suggested resolution:

Change 9.8.2.2 [] paragraph 1 as follows:

...where inline appears if and only if it appears in the unnamed-namespace-definition and all occurrences of unique in each scope in a translation unit are replaced by the same scope-specific identifier, and this identifier differs from all other identifiers in the translation unit.

Notes from the December, 2021 teleconference:

The suggested resolution deals specifically with unnamed namespaces, but there are related examples that do not involve unnamed namespaces. The problem needs to be solved more generally in the specification of lookup.

2555. Ineffective redeclaration prevention for using-declarators

Section: 9.9 [namespace.udecl] Status: drafting Submitter: Christof Meerwald Date: 2022-03-23

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

23

Which functions are called?

Subclause 9.9 [] paragraph 11 specifies:

The set of declarations named by a using-declarator that inhabits a class C does not include member functions and member function templates of a base class that correspond to [and thus would conflict with] a declaration of a function or function template in C.

The definition of "corresponds" considers the type of the implicit object parameter, which is a deviation from the status quo ante for a simple example like this one:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

24

Suggested resolution:

Change in 9.9 [] paragraph 11 as follows:

The set of declarations named by a using-declarator that inhabits a class C does not include member functions and member function templates of a base class that, when considered as members of C, correspond to [and thus would conflict with] a declaration of a function or function template in C.

[ Example:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

25

1817. Linkage specifications and nested scopes

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

According to 9.1 [] paragraph 2,

Unless otherwise stated, utterances in Clause 9 [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 9.11 [] 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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

26

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 9.11 [] 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 [6.4 [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:

...

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

27

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: 9.12.1 [dcl.attr.grammar] Status: drafting Submitter: Daveed Vandevoorde Date: 2013-06-26

The grammar for alignment-specifier in 9.12.1 [] paragraph 1 is:

alignment-specifier: alignas [ type-id ...opt] alignas [ assignment-expression ...opt]

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

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

28

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

In an alignment-specifier [9.12.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

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

29

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.

2223. Multiple alignas specifiers

Section: 9.12.2 [dcl.align] Status: drafting Submitter: Mike Herrick Date: 2016-01-12

According to 9.12.2 [] paragraph 4,

The alignment requirement of an entity is the strictest non-zero alignment specified by its alignment-specifiers, if any; otherwise, the alignment-specifiers have no effect.

It is not clear whether this applies to specifiers within a single declaration, or if it is intended to apply to the union of all declarations.

Similarly, paragraph 6 says,

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.

This only talks about agreement between definitions and non-defining declarations. What about an example where an entity is not defined but is declared with different alignment-specifiers?

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

30

If A is not defined, is this, or should it be, ill-formed?

Notes from the February, 2017 meeting:

CWG agreed that the intent of the wording is that the “strictest” requirement is intended to apply to a single declaration, and the requirement for compatibility should apply to all declarations, whether the entity is defined or not.

2607. Visibility of enumerator names

Section: 10.2 [module.interface] Status: drafting Submitter: Richard Smith Date: 2022-06-28

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

31

It is unclear whether the enumerator name e is or ought to be visible in the other translation unit.

See also issues [friend declarations] and .

CWG 2022-11-10

See 10.2 [] paragraph 7.

1890. Member type depending on definition of member function

Section: 11.4 [class.mem] Status: drafting Submitter: Hubert Tong Date: 2014-03-07

Consider an example like:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

32

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 , , and .]

Additional notes [January, 2023]:

The following example might be related:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

33

1623. Deleted default union constructor and member initializers

Section: 11.4.5 [class.ctor] Status: drafting Submitter: Vinny Romano Date: 2013-02-15

According to 11.4.5 [] paragraph 5,

A defaulted default constructor for class X is defined as deleted if: X is a union-like class that has a variant member with a non-trivial default constructor, ... X is a union and all of its variant members are of const-qualified type [or array thereof], X is a non-union class and all members of any anonymous union member are of const-qualified type [or array thereof],
  • ...

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 11.5 [class.union] paragraphs 2-3 and 9.2.9.2 [] paragraph 2 that would also need to be updated if this restriction is changed.]

It would also be helpful to add a requirement to 11.5 [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 , , , and .

1808. Constructor templates vs default constructors

Section: 11.4.5 [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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

34

According to 9.4.5 [] 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 {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

35

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.

1092. Cycles in overload resolution during instantiation

Section: 11.4.5.3 [class.copy.ctor] 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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

36

If declaring the copy constructor for A is part of instantiating the class, then we need to do overload resolution on D, and thus C. We consider C[const B&], and therefore look to see if there's a conversion from C to B, which instantiates B, which fails because it has a field of type A which is already being instantiated.

Even if we wait until A is considered complete before finalizing the copy constructor declaration, declaring the copy constructor for B will want to look at the copy constructor for A, 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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

37

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 to B we could somehow avoid considering, or even declaring, the B 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.

1548. Copy/move construction and conversion functions

Section: 11.4.5.3 [class.copy.ctor] Status: drafting Submitter: Nikolay Ivchenkov Date: 2012-09-02

The current wording of 11.4.5.3 [] 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 .]

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.

1594. Lazy declaration of special members vs overload errors

Section: 11.4.5.3 [class.copy.ctor] 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 .

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.

2203. Defaulted copy/move constructors and UDCs

Section: 11.4.5.3 [class.copy.ctor] Status: drafting Submitter: Vinny Romano Date: 2015-11-20

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

38

The user-defined move constructor is well-formed because B::a can be initialized via A::operator int[] and A::A[int]; however, Clang and GCC believe a defaulted one would be ill-formed.

What about the following, which is considered well-formed by compilers and calls A::A[C&&]?

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

39

2264. Memberwise copying with indeterminate value

Section: 11.4.5.3 [class.copy.ctor] Status: drafting Submitter: Hubert Tong Date: 2016-05-06

It appears that the following example may have unwanted undefined behavior in C++, although not in C:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

40

The default memberwise copying operation is not specified to be done in a way that is insensitive to indeterminate values.

1499. Missing case for deleted move assignment operator

Section: 11.4.6 [class.copy.assign] Status: drafting Submitter: John Spicer Date: 2012-04-27

Bullet 4 of 11.4.5.3 [] 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 [12.2 [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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

41

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.

2329. Virtual base classes and generated assignment operators

Section: 11.4.6 [class.copy.assign] Status: drafting Submitter: Daveed Vandevoorde Date: 2016-10-31

An example like the following,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

42

is presumably well-formed, even though the copy assignment operator of A is inaccessible in C, because 11.4.6 [] paragraph 12 says that only direct, not virtual, base class object assignment operators are invoked by the generated assignment operator [although there is implementation divergence on this question].

Should the example also be well-formed if A were a direct virtual base of C? That is, if a direct virtual base also has an indirect derivation path, its direct derivation can be ignored for generated assignment operators.

Possibly relevant to this question is the permission for an implementation to assign virtual base class objects more than once:

It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy/move assignment operator.

1977. Contradictory results of failed destructor lookup

Section: 11.4.7 [class.dtor] Status: drafting Submitter: Gabriel Dos Reis Date: 2014-07-21

According to 11.4.7 [] paragraph 12,

At the point of definition of a virtual destructor [including an implicit definition [11.4.5.3 [class.copy.ctor]]], the non-array deallocation function is looked up in the scope of the destructor's class [6.5.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 [9.5 [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 [11.4.11 [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 11.4.11 [] paragraph 7.

2158. Polymorphic behavior during destruction

Section: 11.4.7 [class.dtor] Status: drafting Submitter: Richard Smith Date: 2015-07-13

Consider the following example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

43

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 6.7.3 [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 6.7.3 [] 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 11.4.5 [] 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.

1283. Static data members of classes with typedef name for linkage purposes

Section: 11.4.9.3 [class.static.data] Status: drafting Submitter: Mike Miller Date: 2011-03-29

According to 11.4.9.3 [] 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. [ acknowledged the lack of rationale by removing the specious note in 11.4.9.3 [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: 11.4.9.3 [class.static.data] Status: drafting Submitter: Mike Miller Date: 2013-07-31

Describing the handling of static data members with brace-or-equal-initializers, 11.4.9.3 [] paragraph 3 says,

The member shall still be defined in a namespace scope if it is odr-used [6.3 [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.”

2335. Deduced return types vs member types

Section: 11.4.9.3 [class.static.data] Status: drafting Submitter: John Spicer Date: 2017-01-29

It is not clear how an example like the following should be treated:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

44

The initialization of right is in a context that must be done during the initial parse of the class, but the function body of compute_right is not supposed to be evaluated until the class is complete. Current implementations appear to accept the template case but not the equivalent non-template case. It's not clear why those cases should be treated differently.

If you change the example to include a forward dependency in the body of compute_right, e.g.,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

45

current implementations reject the code, but it's not clear that there is a rationale for the different behavior.

Notes from the March, 2018 meeting:

It was proposed that one direction might be to disallow instantiating member functions while the containing class template is being instantiated. However, overnight implementation experience indicated that this approach breaks seemingly-innocuous and currently-accepted code like:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

46

There was divergence of opinion regarding whether the current rules describe the current behavior for the two original examples or whether additional explicit rules are needed to clarify the difference in behavior between template and non-template examples, as well as whether there should be a difference at all..

Notes from the June, 2018 meeting:

The consensus of CWG was to treat templates and classes the same by "instantiating" delayed-parse regions when they are needed instead of at the end of the class.

See also .

1404. Object reallocation in unions

Section: 11.5 [class.union] Status: drafting Submitter: Nikolay Ivchenkov Date: 2011-10-19

According to 11.5 [] 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:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

47

—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 6.7.3 [] paragraph 7, bullet 3:

  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type

Although paragraph 4 of 11.5 [class.union] is a note and an example, it should at least refer to the lifetime issues described in 6.7.3 [basic.life].

Additional note [October, 2013]:

See also , which suggests possibly changing the restriction in 6.7.3 [basic.life]. If such a change is made, this issue may become moot.

1702. Rephrasing the definition of “anonymous union”

Section: 11.5 [class.union] Status: drafting Submitter: Richard Smith Date: 2013-06-17

11.5 [] paragraph 5 defines an anonymous union as follows:

A union of the form

union { member-specification } ;

is called an anonymous union; it defines an unnamed object of unnamed type.

It is obviously intended that a declaration like

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

48

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:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

49

Additional notes [July, 2023]

This issue is addressed by .

2246. Access of indirect virtual base class constructors

Section: 11.8.3 [class.access.base] Status: drafting Submitter: Vinny Romano Date: 2016-03-08

Consider this example from :

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

50

This example should cause Bar's defaulted default constructor to be deleted, because it does not have access to the injected-class-name Foo.

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 if conversion of the this pointer to a pointer to the base class would succeed.

2588. friend declarations and module linkage

Section: 11.8.4 [class.friend] Status: drafting Submitter: Nathan Sidwell Date: 2022-05-26 Liaison: EWG

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

51

Subclause 11.8.4 [] paragraph 4 gives

1 external linkage:

A function first declared in a friend declaration has the linkage of the namespace of which it is a member [6.6 [basic.link]].

[There is no similar provision for friend classes first declared in a class.]

However, 6.6 [] bullet 4.8 gives it module linkage:

... otherwise, if the declaration of the name is attached to a named module [10.1 [module.unit]] and is not exported [10.2 [module.interface]], the name has module linkage;

Subclause 10.2 [] paragraph 2 does not apply:

A declaration is exported if it is declared within an export-declaration and inhabits a namespace scope or it is a namespace-definition that contains an exported declaration, or a declaration within a header unit [10.3 [module.import]] that introduces at least one name.

Also consider this related example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

52

  • Should the friend's linkage be affected by the linkage of the befriending class? In this example,

    2 would therefore have external linkage, as Y is exported.

  • Or should the friend's linkage be affected by the presence or absence of export on the class definition itself? In this example,

    2 would thus have module linkage.

  • Or should the friend's linkage be determined ignoring any enclosing export and ignoring whether the enclosing class is exported, per 11.8.4 [] paragraph 4 [alone]?
  • Or should the friend's linkage be as-if the declaration inhabited its nearest enclosing namespace scope, without the friend?

See for a similar question about enumerators.

Additional note [May, 2022]:

Forwarded to EWG with paper issue 1253, by decision of the CWG chair.

EWG telecon 2022-06-09

Consensus: "A friend's linkage should be affected by the presence/absence of export on the containing class definition itself, but ONLY if the friend is a definition", pending confirmation by electronic polling.

Proposed resolution [June, 2022]:

  1. Change in 6.6 [] paragraph 4 as follows:
    ... The name of an entity that belongs to a namespace scope that has not been given internal linkage above and that is the name of a variable; or a function; or a named class [11.1 [class.pre]], or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes [9.2.4 [dcl.typedef]]; or a named enumeration [9.7.1 [dcl.enum]], or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes [9.2.4 [dcl.typedef]]; or an unnamed enumeration that has an enumerator as a name for linkage purposes [9.7.1 [dcl.enum]]; or a template

    has its linkage determined as follows:

    if the entity is a function or function template first declared in a friend declaration and that declaration is a definition, the name has the same linkage, if any, as the name of the enclosing class [11.8.4 [class.friend]]; otherwise, if the entity is a function or function template declared in a friend declaration and a corresponding non-friend declaration is reachable, the name has the linkage determined from that prior declaration, otherwise, if the enclosing namespace has internal linkage, the name has internal linkage; otherwise, if the declaration of the name is attached to a named module [10.1 [module.unit]] and is not exported [10.2 [module.interface]], the name has module linkage;
  2. otherwise, the name has external linkage.
  3. Remove 11.8.4 [] paragraph 4: A function first declared in a friend declaration has the linkage of the namespace of which it is a member [6.6 [basic.link]]. Otherwise, the function retains its previous linkage [9.2.2 [dcl.stc]].

EWG electronic poll 2022-06

Consensus for "A friend's linkage should be affected by the presence/absence of export on the containing class definition itself, but ONLY if the friend is a definition [option

2, modified by Jason's suggestion]. This resolves CWG2588." See .

472. Casting across protected inheritance

Section: 11.8.5 [class.protected] Status: drafting Submitter: Mike Miller Date: 16 Jun 2004

Does the restriction in 11.8.5 [class.protected] apply to upcasts across protected inheritance, too? For instance,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

53

I think the rationale for the 11.8.5 [class.protected] restriction applies equally well here — you don't know whether ip points to a D object or not, so D::f can't be trusted to treat the protected B subobject consistently with the policies of its actual complete object type.

The current treatment of “accessible base class” in 11.8.3 [] 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.8.5 [class.protected].

[See also issues and .].

Notes from October 2004 meeting:

The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.8.5 [class.protected].

Proposed resolution [April, 2011]

Change 11.8.3 [] paragraph 4 as follows:

A base class B of N is accessible at R, if an invented public member of B would be a public member of N, or R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of N, or R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or [but not a protected [Footnote: A protected invented member is disallowed here for the same reason the additional check of 11.8.5 [class.protected] is applied to member access: it would allow casting a pointer to a derived class to a protected base class that might be a subobject of an object of a class that is different from the class context in which the reference occurs. —end footnote]] member of P, or there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R.

[Example:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

54

—end example]

1883. Protected access to constructors in mem-initializers

Section: 11.8.5 [class.protected] Status: drafting Submitter: Daveed Vandevoorde Date: 2014-02-26

According to 11.8.5 [] paragraph 1, except when forming a pointer to member,

All other accesses involve a [possibly implicit] object expression [7.6.1.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.

2187. Protected members and access via qualified-id

Section: 11.8.5 [class.protected] Status: drafting Submitter: Hubert Tong Date: 2015-10-16

The following line in the example in 11.8.5 [] paragraph 1 is no longer allowed following the change from :

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

55

The example line ought to work, but none of the bullets in 11.8.3 [] paragraph 5 apply:

A member m is accessible at the point R when named in class N if m as a member of N is public, or m as a member of N is private, and R occurs in a direct member or friend of class N, or m as a member of N is protected, and R occurs in a direct member or friend of class N, or in a member of a class P derived from N, where m as a member of P is public, private, or protected, or there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B.

One aproach might be that 11.8.3 [] bullet 5.3 should also consider friends of a class P derived from N where P is the type of the object expression [if any] or a base class thereof, and m as a member of P is public, protected, or private.

2056. Member function calls in partially-initialized class objects

Section: 11.9.3 [class.base.init] Status: drafting Submitter: Richard Smith Date: 2014-12-11

According to 11.9.3 [] paragraph 16,

Member functions [including virtual member functions, 11.7.3 [class.virtual]] can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator [7.6.1.8 [expr.typeid]] or of a dynamic_cast [7.6.1.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,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

56

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

2403. Temporary materialization and base/member initialization

Section: 11.9.3 [class.base.init] Status: drafting Submitter: Daveed Vandevoorde Date: 2018-12-11

Given the following example,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

57

All implementations treat

1 as an error, invoking the deleted copy constructor, while

2 is accepted. It's not clear from the current wording why they should be treated differently.

Additional note [August, 2022]:

If there are concerns about reuse of tail padding in

1, requiring a copy for some implementation reason, similar concerns should apply to

2 if the data member is declared with [[no_unique_address]].

Furthermore, the following example using a delegating constructor shows implementation divergence:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

58

1517. Unclear/missing description of behavior during construction/destruction

Section: 11.9.5 [class.cdtor] Status: drafting Submitter: Daniel Krügler Date: 2012-07-07

The current wording of 11.9.5 [] 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 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 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 .]

Proposed resolution [August, 2013]:

  1. Change 11.9.5 [] paragraph 1 as follows:
    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 B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

59
  1. Change 11.9.5 [class.cdtor] paragraphs 3-6 as follows:
    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 [11.7.3 [class.virtual]], can be called during construction or destruction [11.9.3 [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 [7.6.1.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 [7.6.1.3 [expr.call]], the typeid operator [7.6.1.8 [expr.typeid]] when applied to a glvalue of polymorphic type, or the dynamic_cast operator [7.6.1.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 B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

60

—end example]

The typeid operator [7.6.1.8 [expr.typeid]] can be used during construction or destruction [11.9.3 [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 [7.6.1.7 [expr.dynamic.cast]] can be used during construction or destruction [11.9.3 [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 B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

61

—end example]

1278. Incorrect treatment of contrived object

Section: 12.2.2.2.2 [over.call.func] Status: drafting Submitter: Nikolay Ivchenkov Date: 2011-03-27

Footnote 127 of 12.2.2.2.2 [] 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 {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

62

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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

63

2564. Conversion to function pointer with an explicit object parameter

Section: 12.2.2.2.3 [over.call.object] Status: drafting Submitter: Christof Meerwald Date: 2022-04-11

Subclause 12.2.2.2.3 [] paragraph 2 considers only those conversion funtions matching a particular grammar pattern. This unintendedly excludes conversion functions with an explicit object parameter [and, as a pre-existing defect,

template concept C = requires [typename T::type x] {

x + 1;
}; static_assert[!C];

64 conversion functions]:

In addition, for each non-explicit conversion function declared in T of the form

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

64 where the optional cv-qualifier-seq is the same cv-qualification as, or a greater cv-qualification than, cv, and where conversion-type-id denotes the type “pointer to function of [P1 , . . . , Pn ] returning R”, or the type “reference to pointer to function of [P1 , . . . , Pn ] returning R”, or the type “reference to function of [P1 , . . . , Pn ] returning R”, a surrogate call function with the unique name call-function and having the form

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

65 is also considered as a candidate function. Similarly, surrogate call functions are added to the set of candidate functions for each non-explicit conversion function declared in a base class of T provided the function is not hidden within T by another intervening declaration. [ Footnote: ...]

For example, there is implementation divergence in handling this example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

66

2089. Restricting selection of builtin overloaded operators

Section: 12.2.2.3 [over.match.oper] Status: drafting Submitter: Hubert Tong Date: 2015-02-26

The candidates selected by 12.2.2.3 [over.match.oper] include built-in candidates that will result in an error if chosen; this was affirmed by . 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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

67

It might be better to adjust the candidate list in 12.2.2.4 [] 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: 12.2.2.7 [over.match.ref] Status: drafting Submitter: Mitsuru Kariya Date: 2014-10-25

Consider the following example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

68

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, 12.2.2.7 [over.match.ref] [used in the relevant bullet of 9.4.4 [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 9.4.4 [dcl.init.ref] should be changed to consider converting constructors in this case.

2108. Conversions to non-class prvalues in reference initialization

Section: 12.2.2.7 [over.match.ref] Status: drafting Submitter: Hubert Tong Date: 2015-03-24

In 12.2.2.7 [over.match.ref], candidates that produce non-class prvalues are considered, although that seems to contradict what 9.4.4 [dcl.init.ref] says. See also .

2194. Impossible case in list initialization

Section: 12.2.2.8 [over.match.list] Status: drafting Submitter: Robert Haberlach Date: 2015-11-04

According to 12.2.2.8 [] 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 9.4.5 [] 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 12.2.2.4 [over.match.ctor].

See also .

Proposed resolution [January, 2017]:

Change 12.2.2.8 [] paragraph 1 as follows:

When objects of non-aggregate class type T are list-initialized such that 9.4.5 [dcl.init.list] specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases: Initially, the candidate functions are the initializer-list constructors [9.4.5 [dcl.init.list]] of the class T and the argument list consists of the initializer list as a single argument. If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list. 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...

Additional notes, February, 2017:

The statement of the issue is incorrect. In an example like

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

69

the rule in question is not used for the initialization of the parameter. However, it is used to determine whether a valid implicit conversion sequence exists for a. It is unclear whether an additional change to resolve this discrepancy is needed or not.

2467. CTAD for alias templates and the deducible check

Section: 12.2.2.9 [over.match.class.deduct] Status: drafting Submitter: Richard Smith Date: 2019-08-12

Given the declarations

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

70

CTAD deduces vector. Then we are asked to perform a check that the arguments of X and Y are deducible from vector.

I think this check should succeed, deducing T = int in the first case and = in the second case, so both declarations should be valid. That seems consistent with what would happen for a non-alias with template parameters that CTAD can't deduce, where there is either a default template argument or the parameter is a pack. But what actually happens is that we're asked to form

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

71

and

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

72

However, both of those partial specializations are ill-formed: a partial specialization can't have default template arguments, and neither of these is more specialized than the primary template, because T / Ts are not used in deducible contexts.

I think we have the wrong model here, and should instead be considering [effectively] whether function template argument deduction would succeed for

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

73

and

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

74

respectively, when given an argument of type AA. That is, get rid of the weird class template partial specialization restrictions, and instead add in the rules from function templates to use default template arguments and to default non-deduced packs to empty packs.

2471. Nested class template argument deduction

Section: 12.2.2.9 [over.match.class.deduct] Status: drafting Submitter: John Spicer Date: 2021-01-26

Consider the following example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

75

The description of CTAD in 12.2.2.9 [over.match.class.deduct] doesn't really specify how nested classes work. If you are supposed to deduce all the enclosing class template arguments, the example is ill-formed because there is no way to deduce T. If you are supposed to consider S::N as having a new constructor template, then it should probably be well-formed.

Notes from the March, 2021 teleconference:

CWG agreed that the intent is to use the partially-instantiated inner template with the explicitly-specified template argument int.

2319. Nested brace initialization from same type

Section: 12.2.4.2 [over.best.ics] Status: drafting Submitter: Richard Smith Date: 2016-09-06

Consider:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

76

a1 and a2 are valid, a3 is ill-formed, because 12.2.4.2 [] bullet 4.5 allows one pair of braces and 12.2.4.2.6 [] paragraph 2 allows a second pair of braces. The implicit conversion sequence from {{a}} to A is a user-defined conversion.

Prior to the list-initialization-from-same-type changes via issues and , a2 was ill-formed like a3.

Is this intended, or did DR2076 not go far enough in reintroducing the restriction? Perhaps a more extreme rule, such as saying that a copy/move constructor is simply not a candidate for list-initialization from a list that contains one element that is itself a list, would work better?

Notes from the July, 2017 meeting:

CWG agreed that the a2 example should be ill-formed but that the a1 example must remain for C compatibility.

2525. Incorrect definition of implicit conversion sequence

Section: 12.2.4.2.1 [over.best.ics.general] Status: drafting Submitter: Jim X Date: 2021-09-25

According to 12.2.4.2.1 [over.best.ics.general] paragraphs 1 and 9,

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in 7.3 [conv], which means it is governed by the rules for initialization of an object or reference by a single expression [9.4 [dcl.init], 9.4.4 [dcl.init.ref]]. If no sequence of conversions can be found to convert an argument to a parameter type, an implicit conversion sequence cannot be formed.

However, 7.3.1 [] paragraph 3 says,

An expression E can be implicitly converted to a type T if and only if the declaration T t=E; is well-formed, for some invented temporary variable t [9.4 [dcl.init]].

This definition is too restrictive in the context of overload resolution's implicit conversion sequences. The intent, as stated in 12.2.1 [over.match.general] note 1, is that overload resolution ignores some factors that would make such an initialization ill-formed, and these are applied only after the best match is determined:

[Note 1: The function selected by overload resolution is not guaranteed to be appropriate for the context. Other restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. —end note]

For example,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

77

The intent is that overload

1 be viable with a valid implicit conversion sequence, making the call at

3 ambiguous, even though the hypothetical declaration

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

78

would be ill-formed.

Proposed resolution [approved by CWG 2022-12-02]:

Change 12.2.4.2.1 [] paragraph 1, merging it with paragraph 2,as follows:

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in 7.3 [conv], which means; it is governed by thus based on the rules for initialization of an object or reference by a single expression [9.4 [dcl.init], 9.4.4 [dcl.init.ref]]. Implicit, except that implicit conversion sequences are concerned only with the type, cv-qualification, and value category of the argument and how these are converted to match the corresponding properties of the parameter. [Note: ... ]

CWG 2023-02-06

Additional drafting is needed to cover e.g. conversions from literal 0 to null pointer constants.

2077. Overload resolution and invalid rvalue-reference initialization

Section: 12.2.4.2.5 [over.ics.ref] Status: drafting Submitter: Richard Smith Date: 2015-01-29

The resolution of broke the following example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

79

Overload resolution selects the A&& overload, but then initialization fails. This seems like a major regression; we're now required to reject

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

80

Should we update 12.2.4.2.5 [over.ics.ref] to match the changes made to 9.4.4 [dcl.init.ref]?

See also .

1536. Overload resolution with temporary from initializer list

Section: 12.2.4.2.6 [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 12.2.4.2.6 [] paragraph 5:

Otherwise, if the parameter is a reference, see 12.2.4.2.5 [over.ics.ref]. [Note: The rules in this section will apply for initializing the underlying temporary for the reference. —end note]

However, 12.2.4.2.5 [over.ics.ref] deals only with expression arguments, not initializer lists:

When a parameter of reference type binds directly [9.4.4 [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 [12.2.4.2 [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 [12.2.4.2.3 [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 12.2.4.2 [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 9.4.4 [dcl.init.ref], which also does not handle initializer lists, and not to 9.4.5 [dcl.init.list].]

Either 12.2.4.2.5 [over.ics.ref] needs to be revised to handle binding references to initializer list arguments or 12.2.4.2.6 [] paragraph 5 needs to be clearer on how the expression specification is intended to be applied to initializer lists.

2492. Comparing user-defined conversion sequences in list-initialization

Section: 12.2.4.2.6 [over.ics.list] Status: drafting Submitter: Jim X Date: 2021-01-11

Consider the following example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

81

According to 12.2.4.2.6 [] paragraph 6,

Otherwise, if the parameter type is std::initializer_list and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor.

In this example, all of the conversions from list elements to the initializer_list template argument type are user-defined conversions. According to 12.2.4.3 [over.ics.rank] bullet 3.3,

User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or they initialize the same class in an aggregate initialization and in either case the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.

Since in both cases the two elements of the initializer-list argument involve different user-defined conversion functions, the two user-defined conversion sequences for the elements cannot be distinguished, so the determination of the “worst conversion” for the two candidates does not consider the second standard conversion sequence. This presumably makes it impossible to distinguish the conversion sequences for the two candidates in the function call, making the call ambiguous.

However, there is implementation divergence on the handling of this example, with g++ reporting an ambiguity and clang, MSVC, and EDG calling the int overload, presumably on the basis that short->int is a promotion while short->bool is a conversion.

Notes from the August, 2021 teleconference:

CWG agreed with the reasoning expressed in the analysis, that conversions involving different user-defined conversion functions cannot be compared, and thus the call is ambiguous. The use of the phrase “worst conversion” is insufficiently clear, however, and requires definition.

Proposed resolution, August, 2021:

Change 12.2.4.2.6 [over.ics.list] paragraphs 5 and 6 as follows:

Otherwise, if the parameter type is std::initializer_list and either the initializer list is empty or all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion worst conversion necessary to convert an element of the list to X, or if defined as follows. If the initializer list has no elements, the worst conversion is the identity conversion. Otherwise, the worst conversion is an implicit conversion sequence for a list element that is not better than any other implicit conversion sequence required by list elements, compared as described in 12.2.4.3 [over.ics.rank]. If more than one implicit conversion sequence satisfies this criterion, then if they are user-defined conversion sequences that do not all contain the same user-defined conversion function or constructor, the worst conversion sequence is the ambiguous conversion sequence [12.2.4.2.1 [over.best.ics.general]]; otherwise, it is unspecified which of those conversion sequences is chosen as worst. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [Example 2:

struct B { virtual void g[]; //

1

}; struct D : B { virtual void f[this D&]; // error: explicit object member function cannot be virtual void g[this D&]; // error: overrides

1; explicit object member function cannot be virtual

};

82

—end example]

Otherwise, if the parameter type is “array of N X ” or “array of unknown bound of X”, if there exists an implicit conversion sequence from each element of the initializer list [and from {} in the former case if N exceeds the number of elements in the initializer list] to X, the implicit conversion sequence is the worst such implicit conversion sequence conversion necessary to convert an element of the list [including, if there are too few list elements, {}] to X, determined as described above for a std::initializer_list with a non-empty initializer list.

CWG 2023-06-13

An ambiguous conversion for the function selected by overload resolution is ill-formed per 12.2.4.2.1 [over.best.ics.general]. Instead of attempting to define "worst conversion" [possibly with focusing on the second standard conversion sequence in user-defined conversions], it might be more consistent overall to use the rules for a succession of function arguments/parameters when comparing list-initializations.

2110. Overload resolution for base class conversion and reference/non-reference

Section: 12.2.4.3 [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 B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

83

The Standard does not appear to specify what happens in this case.

1989. Insufficient restrictions on parameters of postfix operators

Section: 12.4 [over.oper] Status: drafting Submitter: Richard Smith Date: 2014-08-30

According to 12.4.7 [] 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 12.4 [] 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: Clause 13 [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, Clause 13 [] 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, Clause 13 [] 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 , where the phrase "non-template function" in 9.3.4.7 [] paragraph 4 is apparently intended not to include non-template member functions of template classes. See also , which would benefit from understanding nested classes of class templates as templates. Also, see , in which the usage of the phrase "member function template" is questioned.]

Notes from the 4/02 meeting:

Daveed Vandevoorde will propose appropriate terminology.

1463. extern "C" alias templates

Section: 13.1 [temp.pre] Status: drafting Submitter: Daveed Vandevoorde Date: 2011-08-19 Liaison: EWG

Currently 13.1 [] paragraph 6 forbids any template from having C linkage. Should alias templates be exempt from this prohibition, since they do not have any linkage?

Additional note, April, 2013:

It was suggested that relaxing this restriction for alias templates could provide a way of addressing the long-standing lack of a way of specifying a language linkage for a dependent function type [see ].

Rationale [April, 2013]:

CWG felt that this suggested use of alias templates should be considered in a broader context and thus was more appropriate for EWG.

EWG 2022-11-11

extern "C" on a template should be allowed, and should affect only calling convention, but not mangling. This is tracked in github issue cplusplus/papers

1373.

1444. Type adjustments of non-type template parameters

Section: 13.2 [temp.param] Status: drafting Submitter: Johannes Schaub Date: 2012-01-15

The type adjustment of template non-type parameters described in 13.2 [] paragraph 8 appears to be underspecified. For example, implementations vary in their treatment of

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

84

and

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

85

See also issues and .

Additional note, February, 2021:

See the discussion regarding top-level cv-qualifiers on template parameters when determining the type in this compiler bug report.

1635. How similar are template default arguments to function default arguments?

Section: 13.2 [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?

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

86

Also, is the effect on lookup the same? E.g.,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

87

Additional note [November, 2020]:

Paper P1787R6, adopted at the November, 2020 meeting, partially addresses this issue.

2395. Parameters following a pack expansion

Section: 13.2 [temp.param] Status: drafting Submitter: Richard Smith Date: 2018-12-03

The Standard is not clear, and there is implementation divergence, for an example like the following:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

88

The question is whether the 3 is accepted as the argument for I or an error, exceeding the number of arguments for T, which is set as 2 by the template arguments for Tuple_. See also for a related example.

2450. braced-init-list as a template-argument

Section: 13.3 [temp.names] Status: drafting Submitter: Marek Polacek Date: 2019-01-07

Since non-type template parameters can now have class types, it would seem to make sense to allow a braced-init-list as a template-argument, but the grammar does not permit it.

See also issues and .

2043. Generalized template arguments and array-to-pointer decay

Section: 13.4.3 [temp.arg.nontype] Status: drafting Submitter: Richard Smith Date: 2014-11-13

According to 13.4.3 [] 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]: a subobject [6.7.2 [intro.object]], ...

This change breaks an example like

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

89

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:

  • a subobject [6.7.2 [intro.object]] that is not the first element of a complete object of array type,

Note that this resolution also allows an example like

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

90

which may not be exactly what we want.

See also .

2049. List initializer in non-type template default argument

Section: 13.4.3 [temp.arg.nontype] Status: drafting Submitter: Ville Voutilainen Date: 2014-11-20

According to 13.4.3 [] paragraph 1,

A template-argument for a non-type template-parameter shall be a converted constant expression [7.7 [expr.const]] of the type of the template-parameter.

This does not permit an example like:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

91

which seems inconsistent.

See also issues and .

2401. Array decay vs prohibition of subobject non-type arguments

Section: 13.4.3 [temp.arg.nontype] Status: drafting Submitter: John Spicer Date: 2019-02-06

Consider an example like:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

92

Formally, this appears to violate the prohibition of using the address of a subobject as a non-type template argument, since the array reference c in the argument decays to a pointer to the first element of the array. However, at least some implementations accept this example, and at least conceptually the template argument designates the complete object. Should an exception be made for the result of array decay?

See also .

Notes from the July, 2019 meeting

CWG felt that the example should be allowed if the parameter type is a pointer to object type [thus prohibiting void*].

2459. Template parameter initialization

Section: 13.4.3 [temp.arg.nontype] Status: drafting Submitter: Davis Herring Date: 2020-09-21

The initialization of template parameters is severely underspecified. The only descriptions in the existing wording that apply are that the argument is “[converted] to the type of the template-parameter” [13.6 [] bullet 1.3] and, in 13.4.3 [] paragraph 2,

A template-argument for a non-type template-parameter shall be a converted constant expression [7.7 [expr.const]] of the type of the template-parameter.

This omission is particularly important for template parameters of class type with lvalue template parameter objects whose addresses can be examined during construction. See also .

Suggested resolution:

To avoid address-based paradoxes, template arguments for a template parameter of class type C that are not of that type or a derived type are converted to C to produce an exemplar. No restrictions are imposed on the conversion from a template argument to a constructor parameter, since explicit and list-initialization may already be used to limit conversions in a similar fashion. Template arguments that are of such a type are used directly as the exemplar [potentially after a materialization conversion]; the effect is as if the template parameter were of type const C& [except that temporaries are allowed]. [In the latter case, we must impose some restrictions on glvalue template parameters to interpret them.] Each exemplar is used to copy-initialize the template parameter object to which it is [to be] template-argument-equivalent; the initialization is required to produce a template-argument-equivalent value. The multiple initializations of the template parameter object are [required to be] all equivalent and produce no side effects, so it is unobservable which happen.

2057. Template template arguments with default arguments

Section: 13.4.4 [temp.arg.template] Status: drafting Submitter: Jonathan Caves Date: 2014-12-12

It is not clear how to handle an example like:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

93

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.

2398. Template template parameter matching and deduction

Section: 13.4.4 [temp.arg.template] Status: drafting Submitter: Jason Merrill Date: 2016-12-03

Do the changes from P0522R0 regarding template template parameter matching apply to deduction? For example:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

94

In deduction we can determine that P is more specialized than B, then substitute B into P, and then compare B to B. This will allow deduction to succeed, whereas comparing to without this substitution would fail. I suppose this is similar to deducing a type parameter, substituting it into the type of a non-type parameter, then deducing the value of the non-type parameter

Does this make sense? Do we need more wording?

Consider also this example;

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

95

Before this change, partial specialization

1 was not a candidate; now it is, and neither partial specialization is at least as specialized as the other, so we get an ambiguity. It seems that the consistent way to address this would be to use other during partial ordering, so we'd be comparing

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

96

So

1 is more specialized, whereas before this change we chose

2.

2037. Alias templates and template declaration matching

Section: 13.6 [temp.type] Status: drafting Submitter: Richard Smith Date: 2014-11-06

For the following example,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

97

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 and not just A.

See also issues and .

1730. Can a variable template have an unnamed type?

Section: 13.7 [temp.decls] Status: drafting Submitter: Larisse Voufo Date: 2013-08-05

Is it permitted for a variable template to have an unnamed type?

1647. Type agreement of non-type template arguments in partial specializations

Section: 13.7.6 [temp.spec.partial] 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,

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

98

The closest the current wording comes to dealing with this question is 13.7.6.1 [] bullet 9.1:

  • A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.

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 , , and .]

2127. Partial specialization and nullptr

Section: 13.7.6 [temp.spec.partial] Status: drafting Submitter: Faisal Vali Date: 2015-05-18

An example like the following would seem to be plausible:

struct B {

virtual void g[]; // 
# 1 }; struct D : B {
virtual void f[this D&];  // error: explicit object member function cannot be virtual
void g[this D&];          // error: overrides 
# 1; explicit object member function cannot be virtual };

99

This is disallowed by the rule in bullet 9.2 of 13.7.6.1 [temp.spec.partial.general]:

  • The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization.

[See also issues , , and .]

2179. Required diagnostic for partial specialization after first use

Section: 13.7.6.1 [temp.spec.partial.general] Status: drafting Submitter: John Spicer Date: 2015-10-12

According to 13.7.6.1 [] 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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

00

It is unspecified whether the reference to A at

1 is the “first use” or not. If so, A 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: 13.7.6.2 [temp.spec.partial.match] Status: drafting Submitter: Martin Sebor Date: 18 November 2005

In the following example, the template parameter in the partial specialization is non-deducible:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

01

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

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.

Additional note, December, 2021:

The original issue, but not the *

8220;similar issues *

8221; pointed out in the 2006-04 note, was resolved by the changes for and paper P0127R2.

1755. Out-of-class partial specializations of member templates

Section: 13.7.6.4 [temp.spec.partial.member] Status: drafting Submitter: Richard Smith Date: 2013-09-19

According to 13.7.6.4 [] 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 [13.9.2 [temp.inst], 13.9.3 [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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

02

is the last line valid? There is implementation variance on this point. Similarly, for an example like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

03

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.

1286. Equivalence of alias templates

Section: 13.7.8 [temp.alias] Status: drafting Submitter: Gabriel Dos Reis Date: 2011-04-03

was resolved by changing the example in 13.6 [] paragraph 1 from

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

04

to

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

05

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 13.7.8 [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 13.7.8 [] paragraph 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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

06

—end example]
  1. Change 13.6 [] paragraph 1 as follows:
    Two template-ids refer to the same class or function if
... their corresponding template template-arguments refer to the same or equivalent [13.7.8 [temp.alias]] templates.

[Example:

...declares x2 and x3 to be of the same type. Their type differs from the types of x1 and x4.

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

07

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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

08

Notes from the May, 2015 meeting:

See also , 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.

1554. Access and alias templates

Section: 13.7.8 [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 13.7.8 [temp.alias]. For example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

09

Is the substitution of B::foo for foo 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” [13.10.3 [] 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 [21.3.3 [meta.type.synop]]:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

10

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 , dealing with the definition of “immediate context.”

1979. Alias template specialization in template member definition

Section: 13.7.8 [temp.alias] Status: drafting Submitter: Gabriel Dos Reis Date: 2014-07-31

In an example like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

11

should

1 be considered a definition of A::B::f[]?

Analogy with alias-declarations would suggest that it should, but alias template specializations involve issues like SFINAE on unused template parameters [see ] and possibly other complications.

[See also issues , , , and .]

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 .

1980. Equivalent but not functionally-equivalent redeclarations

Section: 13.7.8 [temp.alias] Status: drafting Submitter: Richard Smith Date: 2014-08-04

In an example like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

12

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.

2236. When is an alias template specialization dependent?

Section: 13.7.8 [temp.alias] Status: drafting Submitter: Maxim Kartashev Date: 2016-03-01

There is implementation divergence for this example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

13

See also issues , , and .

2462. Problems with the omission of the typename keyword

Section: 13.8.1 [temp.res.general] Status: drafting Submitter: Mark Hall Date: 2020-12-03

According to 13.8.2 [] paragraph 5,

A qualified-id is assumed to name a type if it is a qualified name in a type-id-only context [see below], or it is a decl-specifier of the decl-specifier-seq of a simple-declaration or a function-definition in namespace scope, member-declaration, parameter-declaration in a member-declaration140, unless that parameter-declaration appears in a default argument, parameter-declaration in a declarator of a function or function template declaration whose declarator-id is qualified, unless that parameter-declaration appears in a default argument,
  • ...

There are two possible problems with this specification. First, consider an example like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

14

Should bullet 5.2.4 be extended to include function pointer and member function pointer declarations, as well as function and function template declarations?

Second, given an example like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

15

Should bullet 5.2.3 be restricted to parameter-declarations of the member being declared, rather than simply “in” such a member-declaration?

Notes from the December, 2020 teleconference:

The second issue was split off into to allow the resolutions to proceed independently.

2468. Omission of the typename keyword in a member template parameter list

Section: 13.8.1 [temp.res.general] Status: drafting Submitter: Mark Hall Date: 2020-12-03

According to 13.8.2 [] paragraph 5,

A qualified-id is assumed to name a type if it is a qualified name in a type-id-only context [see below], or it is a decl-specifier of the decl-specifier-seq of a simple-declaration or a function-definition in namespace scope, member-declaration, parameter-declaration in a member-declaration140, unless that parameter-declaration appears in a default argument, ...

This specification would appear to allow an example like:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

15

The affected parameter-declarations should be only those of the member declarator, not in a member template's template parameter list.

[Note: this issue was spun off from to allow the resolutions to proceed independently.]

1390. Dependency of alias template specializations

Section: 13.8.3.2 [temp.dep.type] Status: drafting Submitter: Johannes Schaub Date: 2011-09-04

According to 13.8.3.2 [] paragraph 8, a type is dependent [among other things] if it is

  • a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent

This applies to alias template specializations, even if the resulting type does not depend on the template argument:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

17

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: 13.8.3.2 [temp.dep.type] Status: drafting Submitter: Jason Merrill Date: 2012-07-17

The correct handling of an example like the following is unclear:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

18

A type used as a base must be complete [11.7 [] 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, 13.8.3 [] 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 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.

2074. Type-dependence of local class of function template

Section: 13.8.3.2 [temp.dep.type] Status: drafting Submitter: Richard Smith Date: 2015-01-20

According to 13.8.3.2 [] 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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

19

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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

20

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.

Additional notes [September, 2022]:

At present, the term "current instantiation" is defined for class templates only, and thus does not apply to function templates.

Moreover, the resolution for this issue should also handle local enums, with particular attention to 9.7.2 [] paragraph 1:

The elaborated-enum-specifier shall not name a dependent type and...

This rule, without amendment, would disallow the following reasonable example if local enums were made dependent types:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

21

2275. Type-dependence of function template

Section: 13.8.3.3 [temp.dep.expr] Status: drafting Submitter: Jason Merrill Date: 2016-06-21

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

22

A member template ought to be dependent only if it depends on template parameters of the current scope, but 13.8.3.3 [] paragraph 3 is silent on the matter.

2487. Type dependence of function-style cast to incomplete array type

Section: 13.8.3.3 [temp.dep.expr] Status: drafting Submitter: Richard Smith Date: 2021-03-12

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

23

I think here T[N...] is not type-dependent, per 13.8.3.3 [] paragraph 3, but should be. [I think T{N...} is type-dependent.] Conversely, I think U{M, M} is type-dependent, per 13.8.3.3 [] paragraph 6, but should not be. [U[M, M] is not type-dependent.]

I think we should say that

simple-type-specifier [ expression-listopt ] typename-specifier [ expression-listopt ] simple-type-specifier braced-init-list typename-specifier braced-init-list

are type-dependent if the type specifier names a dependent type, or if it names an array of unknown bound and the braced-init-list or expression-list is type-dependent.

[I think we could be a little more precise than that in the case where there is no top-level pack expansion: T{M, M} needs to be type-dependent for a general array of unknown bound T due to brace elision, but not in the case where the array element type is a scalar type. And T[M, M] does not need to be type-dependent because direct aggregate initialization can't perform brace elision. But I think the simpler rule is probably good enough.]

Notes from the August, 2021 teleconference:

CWG agreed with the suggested change. There was some support for the “more precise” approach mentioned in the description.

2090. Dependency via non-dependent base class

Section: 13.8.3.5 [temp.dep.temp] Status: drafting Submitter: Maxim Kartashev Date: 2015-02-27

According to 13.8.3.5 [] 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: 13.8.4 [temp.dep.res] Status: drafting Submitter: unknown Date: unknown

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

24 In the class template definition, the declaration of the member function is interpreted as:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

26 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, A::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 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: 13.8.4.1 [temp.point] Status: drafting Submitter: Martin Sebor Date: 17 May 2001

Implementations differ in their treatment of the following code:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

27

Some implementations accept it. At least one rejects it because the instantiation of A requires that B be complete, and it is not at the point at which A is being instantiated.

Erwin Unruh:

In my view the programm is ill-formed. My reasoning:

  • you need a complete type B because you declare a variable in main
  • B contains a member of type A, so you need that complete.
  • A tries to access B::X, which in turn needs B being complete.

So each class needs the other to be complete.

The problem can be seen much easier if you replace the typedef with

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

28

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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

29

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 6.5.5.2 [class.qual] and _N4567_.5.1.1 [] 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 [6.4.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 13.8.4.1 [] 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 is before that of B, not in the middle of B after the declaration of B::X, and consequently a reference to B::X from A 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 specialization] could be something like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

30

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 13.8.4.1 [] 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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

31

Notes from the October 2002 meeting:

This needs work. Moved back to drafting status.

See also issues and .

1845. Point of instantiation of a variable template specialization

Section: 13.8.4.1 [temp.point] Status: drafting Submitter: Richard Smith Date: 2014-01-28

The current wording of 13.8.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.

Additional note, July, 2017:

It has also been observed that there is no definition of the point of instantiation for an alias template. It is not clear that there is a need for normative wording for the point of instantiation of an alias template, but if not, a note explaining its absence would be helpful.

2245. Point of instantiation of incomplete class template

Section: 13.8.4.1 [temp.point] Status: drafting Submitter: Richard Smith Date: 2016-03-08

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

32

According to the wording of , this program is ill-formed, because the single point of instantiation for X is at

1, thus X is an incomplete type even at

2 after the primary template has been completed.

Notes from the December, 2016 teleconference:

The consensus was that references to specializations before the template definition is seen are not points of instantiation.

2497. Points of instantiation for constexpr function templates

Section: 13.8.4.1 [temp.point] Status: drafting Submitter: Richard Smith Date: 2019-07-20

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

33

There are two points of instantiation for f. At

1, the template isn't defined, so it cannot be instantiated there. At

2, it's too late, as the definition was needed when parsing the type of k.

Should we also treat the point of definition of [at least] a constexpr function template as a point of instantiation for all specializations that have a point of instantiation before that point? Note the possible interaction of such a resolution with 13.8.4.1 [] paragraph 7:

If two different points of instantiation give a template specialization different meanings according to the one-definition rule [6.3 [basic.def.odr]], the program is ill-formed, no diagnostic required.

Notes from the November, 2021 teleconference:

Another possibility for a point of instantiation, other than the definition of the template, would be the point at which the function is called. Similar questions have been raised regarding the points at which variables are initialized [] and constexpr functions are defined [].

2202. When does default argument instantiation occur?

Section: 13.9.2 [temp.inst] Status: drafting Submitter: Richard Smith Date: 2015-11-19

According to 13.9.2 [] paragraph 11,

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared [7.5.5.2 [expr.prim.lambda.closure]] — and therefore its associated namespaces — remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.

Some details are not clear from this description. For example, given

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

34

does “used” mean odr-used or used in any way? Is a failure of default argument instantiation in the immediate context of the call or is a failure a hard error? And does it apply only to function templates, as it says, or should it apply to member functions of class templates? There is implementation divergence on these questions.

Notes from the March, 2018 meeting:

CWG felt that such errors should be substitution failures, not hard errors.

2222. Additional contexts where instantiation is not required

Section: 13.9.2 [temp.inst] Status: drafting Submitter: CWG Date: 2016-01-11

According to 13.9.2 [] paragraph 6,

If the function selected by overload resolution [12.2 [over.match]] can be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.

There are other contexts in which a smart implementation could presumably avoid instantiations, such as when doing argument-dependent lookup involving a class template specialization when the template definition contains no friend declarations or checking base/derived relationships involving incomplete class template definitions. It would be helpful to enumerate such contexts.

2263. Default argument instantiation for friends

Section: 13.9.2 [temp.inst] Status: drafting Submitter: Hubert Tong Date: 2016-05-04

The instantiation of default arguments for friends defined in a templated entity is not covered by 13.7.1 [] paragraph 3 or 13.9.2 [] paragraph 2. Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

35

There is implementation divergence in the treatment of this example.

Notes from the December, 2016 teleconference:

This issue should be resolved by the resolution of .

2265. Delayed pack expansion and member redeclarations

Section: 13.9.2 [temp.inst] Status: drafting Submitter: Hubert Tong Date: 2016-05-11

It is not clear how to handle parameter packs that are expanded during instantiation in parallel with those that are not yet concrete. In particular, does the following example require a diagnostic?

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

36

Notes from the March, 2018 meeting:

CWG felt that ill-formed, no diagnostic required was the correct approach.

2596. Instantiation of constrained non-template friends

Section: 13.9.2 [temp.inst] Status: drafting Submitter: David Friberg Date: 2022-06-03

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

37

The current wording does not seem to cover what happens for this case. In particular, 13.9.2 [] paragraph 17 does not cover constrained non-template friends.

See also the Itanium ABI .

Suggested resolution:

  1. Change in 13.7.5 [] paragraph 9 as follows:
    A non-template friend declaration with a requires-clause shall be a definition. A friend function template with a constraint that depends on a template parameter from an enclosing template shall be a definition. Such a constrained friend function or function template declaration does not declare the same function or function template as a declaration in inhabiting any other scope.
  2. Change in 13.9.2 [] paragraph 17 as follows: The type-constraints and requires-clause of a template specialization or member templated function are not instantiated along with the specialization or function itself, even for a member function of a local class; substitution into the atomic constraints formed from them is instead performed as specified in 13.5.3 [temp.constr.decl] and 13.5.2.3 [temp.constr.atomic] when determining whether the constraints are satisfied or as specified in 13.5.3 [temp.constr.decl] when comparing declarations.

[ Note 7: ... ]

[ Example 10: ... ]

[ Example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

38

-- end example ]

[ Example 11: ... ]

CWG 2022-11-10

The friend definitions should conflict with friend definitions from other instantiations of the same class template, consistent with how non-constrained friends would work. Note that the enclosing dependent class type does not appear in the friend function's signature, which is unusual.

1665. Declaration matching in explicit instantiations

Section: 13.9.3 [temp.explicit] Status: drafting Submitter: Richard Smith Date: 2013-04-19

Consider a case like

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

39

or

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

40

Presumably in both these cases the explicit instantiation should refer to the template and not to the non-template; however, 13.7.3 [] 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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

41

If the explicit instantiation directive applies to the constructor template, there is no way to explicitly instantiate the copy constructor.

2421. Explicit instantiation of constrained member functions

Section: 13.9.3 [temp.explicit] Status: drafting Submitter: Casey Carter Date: 2019-07-16

An explicit instantiation of a class template specialization also explicitly instantiates member functions of that class template specialization whose constraints are satisfied, even those that are not callable because a more-constrained overload exists which would always be selected by overload resolution. Ideally, we would not explicitly instantiate definitions of such uncallable functions.

Notes from the August, 2020 teleconference:

CWG felt that the concept of “eligible” might form a basis for the resolution of this issue.

2501. Explicit instantiation and trailing requires-clauses

Section: 13.9.3 [temp.explicit] Status: drafting Submitter: Davis Herring Date: 2021-08-09

CWG determined that was not a defect. However, the discussion uncovered an issue regarding the handling of an explicit instantiation of a class template containing such members. According to 13.9.3 [] paragraph 10,

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind [declaration or definition] of each of its direct non-template members that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation [13.5.3 [temp.constr.decl], 13.5.2 [temp.constr.constr]], except as described below.

Paragraph 12 says,

An explicit instantiation of a prospective destructor [11.4.7 [class.dtor]] shall correspond to the selected destructor of the class.

Perhaps the virtual and constrained members could be handled in an analogous fashion.

Notes from the November, 2021 teleconference:

is being reopened due to subsequent comments.

CWG 2022-11-10

For each explicit instantiation, there shall be exactly one member whose constraints are more specialized than any other member with the same signature. Use the "address of function" model to determine this member.

529. Use of template with “explicitly-specialized” class templates

Section: 13.9.4 [temp.expl.spec] Status: drafting Submitter: James Widman Date: 16 August 2005

Paragraph 17 of 13.9.4 [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 13.9.4 [] paragraph 17 as indicated:
    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:...
  1. Change 13.9.4 [] paragraph 18 as indicated:
    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 13.9.4 [] paragraph 17 as follows:
    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:...
  1. Change 13.9.4 [] paragraph 18 as follows:
    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:...

1840. Non-deleted explicit specialization of deleted function template

Section: 13.9.4 [temp.expl.spec] Status: drafting Submitter: Richard Smith Date: 2014-01-19

The resolution of permits a non-deleted explicit specialization of a deleted function template. For example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

42

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: 13.9.4 [temp.expl.spec] Status: drafting Submitter: Richard Smith Date: 2014-08-31

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.

2409. Explicit specializations of constexpr static data members

Section: 13.9.4 [temp.expl.spec] Status: drafting Submitter: Mike Miller Date: 2019-04-29

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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

43

Some implementations accept this, apparently on the basis of allowing and ignoring a redeclaration of a constexpr static data member outside its class, although there is implementation divergence. Most or all implementations, however, diagnose an attempt to use such a specialization in a constant context.

Should it be required to have a definition of the explicit specialization in order to declare it outside the class in such cases?

In addition, most or all implementations accept a version of the example in which the explicit specialization contains an initializer, including allowing its use in constant contexts:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

44

This would seem to be disallowed both by 11.4.9.3 [] paragraph 3,

An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer [this usage is deprecated; see _N4778_.D.4 [depr.static_constexpr]].

which prohibits an initializer, and 13.9.4 [] paragraph 2,

An explicit specialization may be declared in any scope in which the corresponding primary template may be defined [_N4868_.9.8.2.3 [namespace.memdef], 11.4 [class.mem], 13.7.3 [temp.mem]].

since the definition of a constexpr static data member is inside the class.

Notes from the May, 2019 teleconference:

These examples should behave in the same way as if the class were templated: instantiate the declaration and the definition of the static data member separately. The first example should be ill-formed, because the explicit specializaation does not have an initializer.

2055. Explicitly-specified non-deduced parameter packs

Section: 13.10.2 [temp.arg.explicit] Status: drafting Submitter: Jonathan Caves Date: 2014-12-09

According to 13.10.2 [] paragraph 3,

Trailing template arguments that can be deduced [13.10.3 [temp.deduct]] or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack [13.7.4 [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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

45

See also .

1172. “instantiation-dependent” constructs

Section: 13.10.3 [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 ] was deemed relevant for this issue:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

46

1322. Function parameter type decay in templates

Section: 13.10.3 [temp.deduct] Status: drafting Submitter: Jason Merrill Date: 2011-05-19

The discussion of seemed to have settled on the approach of doing the 9.3.4.6 [dcl.fct] transformations immediately to the function template declaration, so that the original form need not be remembered. However, the example in 13.10.3 [] paragraph 8 suggests otherwise:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

47

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: 13.10.3 [temp.deduct] Status: drafting Submitter: John Spicer Date: 2012-10-31

According to 13.10.3 [] 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,

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

48

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 .

1513. initializer_list deduction failure

Section: 13.10.3.2 [temp.deduct.call] Status: drafting Submitter: Steve Adamczyk Date: 2012-06-28

According to 13.10.3.2 [] paragraph 1,

If removing references and cv-qualifiers from P gives std::initializer_listfoo is less clear, because the transformation to the form [*[E1]].E2 occurs later.

Suggested resolution:

Move a part of 7.6.1.5 [] paragraph 1 to before paragraph 3 and edit as follows:

A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template, and then followed by an id-expression, is a postfix expression. The postfix expression before the dot or arrow is evaluated; [ Footnote: ... ] the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression. [Note 1: If the keyword template is used, the following unqualified name is considered to refer to a template [13.3 [temp.names]]. If a simple-template-id results and is followed by a ::, the id-expression is a qualified-id. —end note]

For the first option [dot] the first expression shall be a glvalue. For the second option [arrow] the first expression shall be a prvalue having pointer type. The expression E1->E2 is converted to the equivalent form [*[E1]].E2; the remainder of 7.6.1.5 [expr.ref] will address only the first option [dot]. [ Footnote: ... ]

The postfix expression before the dot is evaluated; [ Footnote: ... ] the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression.

742. Postfix increment/decrement with long bit-field operands

Section: 7.6.1.6 [expr.post.incr] Status: open Submitter: Mike Miller Date: 11 November, 2008

Given the following declarations:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

85

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 [7.6.1.6 [expr.post.incr]], which means that the usual arithmetic conversions [Clause 7 [] 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 [7.3.7 [] 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: 7.6.1.8 [expr.typeid] Status: open Submitter: Jens Maurer Date: 01 May 2001

The original proposed resolution for included changing extended_type_info [7.6.1.8 [] 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.

1954. typeid null dereference check in subexpressions

Section: 7.6.1.8 [expr.typeid] Status: open Submitter: David Majnemer Date: 2014-06-23

According to 7.6.1.8 [] paragraph 2,

If the glvalue expression is obtained by applying the unary * operator to a pointer69 and the pointer is a null pointer value [7.3.12 [conv.ptr]], the typeid expression throws an exception [14.2 [except.throw]] of a type that would match a handler of type std::bad_typeid exception [17.7.5 [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: 7.6.1.9 [expr.static.cast] Status: open Submitter: Richard Smith Date: 2014-11-19

According to 7.6.1.9 [] paragraph 1,

The static_cast operator shall not cast away constness [7.6.1.11 [expr.const.cast]].

However, this phrasing is problematic in the context of a C-style cast like the following:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

86

The intent of 7.6.3 [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 7.6.1.9 [] 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.

2780. reinterpret_cast to reference to function types

Section: 7.6.1.10 [expr.reinterpret.cast] Status: open Submitter: Lauri Vasama Date: 2023-08-07

Subclause 7.6.1.10 [] paragraph 11 specifies:

A glvalue of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result is that of *reinterpret_cast[p] where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors [11.4.5 [class.ctor]] or conversion functions [11.4.8 [class.conv]] are called. [ Footnote: ... ]

The wording does not cover references to function type, only references to object types. All major implementations accept the following example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

87

Possible resolution:

Change in 7.6.1.10 [] paragraph 11 as follows:

A glvalue of type T1, designating an object entity x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result is that of *reinterpret_cast[p] where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors [11.4.5 [class.ctor]] or conversion functions [11.4.8 [class.conv]] are called. [ Footnote: ... ]

2668. co_await in a lambda-expression

Section: 7.6.2.4 [expr.await] Status: open Submitter: Jim X Date: 2022-12-12

Subclause 7.6.2.4 [] paragraph 2 disallows an await-expression to appear in the body of a lambda-expression:

An await-expression shall appear only in a potentially-evaluated expression within the compound-statement of a function-body outside of a handler [14.1 [except.pre]]. ...

This is probably unintended.

Possible resolution:

Change in 7.6.2.4 [] paragraph 2 as follows:

An await-expression shall appear only in as a potentially-evaluated expression within the compound-statement of a function-body or lambda-expression, in either case outside of a handler [14.1 [except.pre]]. ...

2609. Padding in class types

Section: 7.6.2.5 [expr.sizeof] Status: open Submitter: Jim X Date: 2022-07-19

Class types may have padding, influencing the result of sizeof. It is unclear whether the placement and amount of padding is implementation-defined, unspecified, or something else. If it is unspecified, the limits of permissible behavior are unclear. Empty classes might need special consideration.

Suggested resolution:

Change in 7.6.2.5 [] paragraph 2

... When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array. The amount and placement of padding in a class type is unspecified. The result of applying sizeof to a potentially-overlapping subobject is the size of the type, not the size of the subobject. [ Footnote: ... ]

267. Alignment requirement for new-expressions

Section: 7.6.2.8 [expr.new] Status: open Submitter: James Kuyper Date: 4 Dec 2000

Requirements for the alignment of pointers returned by new-expressions are given in 7.6.2.8 [] 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 [6.8 [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 7.6.1.10 [] 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.

Additional note [June, 2022]:

Subclause 6.7.6 [] paragraph 4 specifies:

... Every alignment value shall be a non-negative integral power of two.

Thus, the situation that a stricter alignment is not an integral multiple of a weaker alignment does not arise.

1628. Deallocation function templates

Section: 7.6.2.8 [expr.new] Status: open Submitter: Richard Smith Date: 2013-02-22

According to 7.6.2.8 [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 6.7.5.5.3 [] 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 7.6.2.8 [] 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 .]

2532. Kind of pointer value returned by new T[0]

Section: 7.6.2.8 [expr.new] Status: open Submitter: Andrey Erokhin Date: 2022-02-17

A pointer value must have one of the kinds specified in 6.8.4 [] paragraph 3:

Every value of pointer type is one of the following: a pointer to an object or function [the pointer is said to point to the object or function], or a pointer past the end of an object [7.6.6 [expr.add]], or the null pointer value for that type, or an invalid pointer value.

When allocating an array with no elements, 7.6.2.8 [] paragraph 10 is silent on the kind of pointer value returned:

When the allocated type is “array of N T” [that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type], the new-expression yields a prvalue of type “pointer to T” that points to the initial element [if any] of the array. Otherwise, let T be the allocated type; the new-expression is a prvalue of type “pointer to T” that points to the object created.

Related to that, are p and q allowed to compare equal in the following example?

const char [&r] [] = "foo";

const char a[] = {'f', 'o', 'o', '\0'}; int main[] { assert[&r == &a]; // allowed not to fail? }

88

Some implementations return a pointer past the array cookie for empty arrays, which can compare equal to a pointer to an object obtained from an unrelated allocation. However, if new T[0] is specified to yield a pointer to an object, this behavior violates the rule that pointers to disjoint objects with overlapping lifetimes must not compare equal.

2592. Missing definition for placement allocation/deallocation function

Section: 7.6.2.8 [expr.new] Status: open Submitter: Jim X Date: 2022-04-14

Subclause 7.6.2.8 [expr.new] has multiple references to "placement allocation function" and "placement deallocation function", but those terms are never defined. The term "usual deallocation function" is defined in 6.7.5.5.3 [] paragraph 3:

... A usual deallocation function is a deallocation function whose parameters after the first are optionally, a parameter of type std::destroying_delete_t, then optionally, a parameter of type std::size_t, [ Footnote: ... ] then
  • optionally, a parameter of type std::align_val_t.

Possible resolution:

  1. Split 6.7.5.5.2 [] paragraph 1 and change it as follows:
    ... The value of the first parameter is interpreted as the requested size of the allocation. A usual allocation function is an allocation function with no parameters after the first or with a single parameter of type std::align_val_t after the first.
An allocation function can be a function template. ...

CWG 2023-06-17

Replace "placement allocation / deallocation function" with "not a usual allocation / deallocation function".

196. Arguments to deallocation functions

Section: 7.6.2.9 [expr.delete] Status: open Submitter: Matt Austern Date: 20 Jan 2000

7.6.2.8 [] 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.

6.7.5.5.3 [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. [7.6.2.9 [] 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 7.6.2.9 [expr.delete] to clarify that any offset added in an array new-expression must be subtracted in the array delete-expression.

2728. Evaluation of conversions in a delete-expression

Section: 7.6.2.9 [expr.delete] Status: open Submitter: Jiang An Date: 2023-05-05

Subclause 7.6.2.9 [] paragraph 4 specifies:

The cast-expression in a delete-expression shall be evaluated exactly once.

Due to the reference to the syntactic non-terminal cast-expression, it is unclear whether that includes the conversion to pointer type specified in 7.6.2.9 [] paragraph 2.

Possible resolution:

  1. Change in 7.6.2.9 [] paragraph 1 and paragraph 2 as follows:
    ... The operand shall be of class type or a prvalue of pointer to object type or of class type. If of class type, the operand is contextually implicitly converted [7.3 [conv]] to a prvalue pointer to object type. [ Footnote: ... ] The converted operand is used in place of the original operand for the remainder of this subclause. The delete-expression has type void.

If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this subclause. ...

  1. Change in 7.6.2.9 [] paragraph 4 as follows: The cast-expression in operand of a delete-expression shall be evaluated exactly once.

2758. What is "access and ambiguity control"?

Section: 7.6.2.9 [expr.delete] Status: open Submitter: CWG Date: 2023-06-12

Subclause 7.6.2.9 [] paragraph 12 specifies:

Access and ambiguity control are done for both the deallocation function and the destructor [11.4.7 [class.dtor], 11.4.11 [class.free]].

It is unclear what that means. In particular, ambiguity checking is part of overload resolution, and access checking requires a point of reference.

Possible resolution:

  1. Change in 7.6.2.9 [] paragraph 6 as follows:
    If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function [see below] is not a destroying operator delete, evaluating the delete-expression will invoke invokes the destructor [if any] for the object or the elements of the array being deleted. The destructor shall be accessible from the point where the delete-expression appears. In the case of an array, the elements will be are destroyed in order of decreasing address [that is, in reverse order of the completion of their constructor; see 11.9.3 [class.base.init]].
  2. Change in 7.6.2.9 [] paragraph 10 If more than one deallocation function is found, the The deallocation function to be called is selected as follows:
  3. ...

Unless the deallocation function is selected at the point of definition of the dynamic type's virtual destructor, the selected deallocation function shall be accessible from the point where the delete-expression appears.

  1. Remove 7.6.2.9 [] paragraph 12: Access and ambiguity control are done for both the deallocation function and the destructor [11.4.7 [class.dtor], 11.4.11 [class.free]].

2768. Assignment to scalar with a braced-init-list

Section: 7.6.19 [expr.ass] Status: open Submitter: Shafik Yaghmour Date: 2023-07-06

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

89

1 first initializes a temporary of type E and then assigns that to e. For

2, 7.6.19 [] bullet 8.1 specifies that

2 is equivalent to

1:

A braced-init-list may appear on the right-hand side of an assignment to a scalar, in which case the initializer list shall have at most a single element. The meaning of x = {v}, where T is the scalar type of the expression x, is that of x = T{v}. The meaning of x = {} is x = T{}. ...

However, there is no syntactic hint that

2 would invoke direct-initialization, and in fact gcc, icc, and MSVC reject

2, but clang accepts.

Possible resolution:

Change in 7.6.19 [] bullet 8.1 as follows:

A braced-init-list may appear on the right-hand side of an assignment to a scalar, in which case the initializer list shall have at most a single element. The meaning of x = {v} braced-init-list is x = t, where t is some invented temporary variable declared and initialized as T t = braced-init-list, where T is the scalar type of the expression x, is that of x = T{v}. The meaning of x = {} is x = T{}. ...

1256. Unevaluated operands are not necessarily constant expressions

Section: 7.7 [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.

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

Section: 7.7 [expr.const] Status: open 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:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

90

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

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

91

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

2192. Constant expressions and order-of-eval undefined behavior

Section: 7.7 [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.

2301. Value-initialization and constexpr constructor evaluation

Section: 7.7 [expr.const] Status: open Submitter: Daveed Vandevoorde Date: 2016-04-18

Consider the following example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

92

Value-initialization of the object created by B[] zero-initializes the object [9.4 [] bullet 8.2], which should mean that the ++x in the mem-initilizer for A operates on a zero-initialized object, but current implementations reject this code as non-constant. It is not clear what in the current wording justifies this treatment.

2456. Viable user-defined conversions in converted constant expressions

Section: 7.7 [expr.const] Status: open Submitter: Mike Miller Date: 2020-08-17

Consider an example like the following:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

93

According to 9.3.4.5 [] paragraph 1, the array bound expression

shall be a converted constant expression of type std::size_t [7.7 [expr.const]].

The user-defined conversion to float would involve a floating-integral conversion [7.3.11 [conv.fpint]; however, such a conversion is not permitted by the list of acceptable conversions in 7.7 [] paragraph 10:

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions [7.3.2 [conv.lval]], array-to-pointer conversions [7.3.3 [conv.array]], function-to-pointer conversions [7.3.4 [conv.func]], qualification conversions [7.3.6 [conv.qual]], integral promotions [7.3.7 [conv.prom]], integral conversions [7.3.9 [conv.integral]] other than narrowing conversions [9.4.5 [dcl.init.list]], null pointer conversions [7.3.12 [conv.ptr]] from std::nullptr_t, null member pointer conversions [7.3.13 [conv.mem]] from std::nullptr_t, and function pointer conversions [7.3.14 [conv.fctptr]], and where the reference binding [if any] binds directly.

It is not clear whether this list is intended to restrict the set of viable user-defined conversions, and there is implementation divergence on this point: clang accepts the example above, while g++ rejects it, presumably on the basis of an ambiguous conversion.

Notes from the August, 2020 teleconference:

No direction was established pending information about why the example is accepted by clang.

Additional note, December, 2020:

The clang behavior turns out to have been an oversight, corrected in the current version, so the example is now rejected by both compilers. However, it is unclear that this is desirable. In particular, given the example above, a can be used without error as a bit-field width, as an enumerator value, and as the operand of alignas. Presumably the difference between these integral constant expression contexts and an array bound is the fact that the target type is known to be size_t. However, both bit-field widths and alignas operands are also required to be non-negative. Furthermore, the definition of an “erroneous” array bound in 7.6.2.8 [] paragraph 9 goes to awkward lengths to check for negative values as the result of user-defined conversions, which might argue in favor of reconsidering the converted constant expression treatment of array bounds.

Notes from the February, 2021 teleconference:

CWG agreed with the considerations in the December, 2020 note, feeling that the difference in treatment between integral constant expressions and a converted constant expression to a specific integral type is somewhat gratuitous. However, it was felt that code like that of the example was unlikely to occur often in real-world code.

2536. Partially initialized variables during constant initialization

Section: 7.7 [expr.const] Status: open Submitter: Barry Revzin Date: 2022-02-21 Liaison: EWG

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

94

The treatment of this example changed with P1331R2 [Permitting trivial default initialization in constexpr contexts], adopted 2019-07. Prior to this paper, the default constructor of A was not constexpr because it left a data member uninitialized. With paper P1331, the restriction was shifted to reading uninitialized objects during constant evaluation, and the variable a now satisfies the requirements for "constant-initialized" in 7.7 [] paragraph 2:

A variable or temporary object o is constant-initialized if either it has an initializer or its default-initialization results in some initialization being performed, and the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

Zero-initialization is not performed prior to constant-initialization per 6.9.3.2 [] paragraph 2:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized [7.7 [expr.const]]. If constant initialization is not performed, a variable with static storage duration [6.7.5.2 [basic.stc.static]] or thread storage duration [6.7.5.3 [basic.stc.thread]] is zero-initialized [9.4 [dcl.init]]. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.

Thus,

1 is valid and a is statically initialized, but a.y would remain uninitialized, which is surprising for an object with static storage duration.

Current implementations diagnose an error at

1, because the variable a is actually not considered to be constant-initialized.

This issue is closely related to .

Suggested resolution [SUPERSEDED]:

Change in 7.7 [] paragraph 2:

A variable or temporary object o is constant-initialized if either it has an initializer or its default-initialization results in some initialization being performed, and every non-variant non-static data member and base class subobject is initialized [11.9.3 [class.base.init]], and
  • the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

Alternative suggested resolution [March, 2022] [SUPERSEDED]:

Change in 7.7 [] paragraph 11 as follows:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression [as defined below], or a prvalue core constant expression whose value satisfies the following constraints: if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression, if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object [7.6.6 [expr.add]], the address of a non-immediate function, or a null pointer value, if the value is of pointer-to-member-function type, it does not designate an immediate function, and if the value is an object of class or array type, each subobject is initialized [11.9.3 [class.base.init]] and satisfies these constraints for the value.

Notes from the November, 2022 meeting

CWG preferred to zero-initialize a.y in the example, and keep

1 well-formed.

Possible resolution:

Change in 6.9.3.2 [] paragraph 2 as follows:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized [7.7 [expr.const]]. If constant initialization is not performed, a A variable with static storage duration [6.7.5.2 [basic.stc.static]] or thread storage duration [6.7.5.3 [basic.stc.thread]] or a subobject thereof is zero-initialized [9.4 [dcl.init]] if constant initialization is not performed or if it does not initialize that subobject. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before [6.9.2.2 [intro.races]] any dynamic initialization.

CWG 2022-12-02

The resolution shown above would leave padding bits uninitialized. In contrast, zero-initialization does set padding bits to 0 to possibly facilitate memcmp. Additional example:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

95

2022-12-03

Forwarded to EWG with cplusplus/papers

1380.

Additional notes [January, 2023]

The standard does not guarantee stable results when reading padding bits, i.e. bits that are not part of the value representation of some in-lifetime object. In C, explicit rules keep padding bits stable; they are allowed to change only if a store to any class member occurs.

2545. Transparently replacing objects in constant expressions

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2022-03-05

7.7 [] paragraph 6 specifies that std::construct_at can be used during constant evaluation:

Similarly, the evaluation of a call to std::construct_at or std::ranges::construct_at does not disqualify E from being a core constant expression unless the first argument, of type T*, does not point to storage allocated with std::allocator or to an object whose lifetime began within the evaluation of E, or the evaluation of the underlying constructor call disqualifies E from being a core constant expression.

Apparently, implementations are required to track whether an object is transparently replaceable [6.7.3 [] paragraph 8] during constant evaluation to satisfy 7.7 [] bullet 5.8, which requires that undefined behavior be detected and rejected during constant evaluation:

... an operation that would have undefined behavior as specified in Clause 4 through Clause 15;
  • ...

For example,

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

96

No known implementation diagnoses the violation of the rules for transparently replaceable in the following example, but there is implementation divergence for the results of f[]:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

97

2559. Defaulted consteval functions

Section: 7.7 [expr.const] Status: open Submitter: Aaron Ballman Date: 2022-03-29

Consider:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

98

A consteval function should always be evaluated at compile time and never fall back to runtime, thus all four cases should be rejected. is related.

2633. typeid of constexpr-unknown dynamic type

Section: 7.7 [expr.const] Status: open Submitter: Jim X Date: 2022-09-18

Consider the example in 7.7 [] paragraph 7:

const char [&r] [] = "foo"; const char a[] = {'f', 'o', 'o', '\0'}; int main[] {

assert[&r == &a];   // allowed not to fail?
}

99

The comment in the example seems not to be backed by normative text. In particular, the dynamic type of dc is constexpr-unknown per 7.7 [] paragraph 7:

During the evaluation of an expression E as a core constant expression, all id-expressions and uses of *this that refer to an object or reference whose lifetime did not begin with the evaluation of E are treated as referring to a specific instance of that object or reference whose lifetime and that of all subobjects [including all union members] includes the entire constant evaluation. For such an object that is not usable in constant expressions, the dynamic type of the object is constexpr-unknown. ...

Thus, typeid[dc] is not a core constant expression per 7.7 [] bullet 5.26:

* a dynamic_cast [7.6.1.7 [expr.dynamic.cast]] or typeid [7.6.1.8 [expr.typeid]] expression on a glvalue that refers to an object whose dynamic type is constexpr-unknown or that would throw an exception;

2656. Converting consteval lambda to function pointer in non-immediate context

Section: 7.7 [expr.const] Status: open Submitter: Hubert Tong Date: 2022-11-11

Converting a consteval lambda to a function pointer in a non-immediate context should be immediately-escalating.

Currently, this is well-formed:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

00

Suggested resolution:

Make the conversion function of a lambda whose call operator is an immediate function also an immediate function.

2702. Constant destruction of reference members

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2023-02-13

says that a constexpr reference bound to a lifetime-extended temporary is required to have constant destruction. However, that rule overlooks constexpr objects with a reference member bound to a lifetime-extended temporary, recursively, and the case for std::initializer_list lifetime extension.

2734. Immediate forward-declared function templates

Section: 7.7 [expr.const] Status: open Submitter: Corentin Jabot Date: 2023-05-10

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

01

The standard is silent as to what happen if no function body is available at the point where we need to determine whether a constexpr function template is an immediate function template.

Furthermore, there is no wording that taking the address of a constexpr function template specialization instantiates its body to determine whether it is an immediate function, unlike the handling of auto return types.

Subclause 13.9.2 [] paragraph 5 specifies:

Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program. ...

See also .

2740. Too many objects have constexpr-unknown type

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2023-02-10

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

02

The example had been valid, but became invalid under P2280. The same happened for dynamic_cast. Furthermore, P2280 seems to be missing a change for static_cast from base to derived and for pointer-to-member access expressions, which both depend on the dynamic type and have historically been required to work.

Maybe a better rule would be that objects [not references] whose lifetime did not begin within E are treated as having a dynamic type that is their declared type.

2760. Defaulted constructor that is an immediate function

Section: 7.7 [expr.const] Status: open Submitter: Corentin Jabot Date: 2023-07-08

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

03

Is S an immediate function?

The relevant specification is in 7.7 [] paragraph 18:

An immediate function is a function or constructor that is declared with the consteval specifier, or an immediate-escalating function F whose function body contains an immediate-escalating expression E such that E's innermost enclosing non-block scope is F's function parameter scope.

Suggested resolution:

Change in 7.7 [] paragraph 18 as follows:

An immediate function is a function or constructor that is declared with the consteval specifier, or an immediate-escalating function F whose function body contains an immediate-escalating expression E such that E's innermost enclosing non-block scope is F's function parameter scope, or
  • an immediate-escalating default constructor of a class which has at least one non-static data member with an immediate-escalating default member initializer.

Alternative possible resolution:

  1. Change in 7.7 [] paragraph 18 as follows:
    An immediate function is a function or constructor that is declared with the consteval specifier, or an immediate-escalating function F whose function body contains an immediate-escalating expression E such that E's innermost enclosing non-block scope is F's function parameter scope. [ Note: Default member initializers used to initialize a base or member subobject [11.9.3 [class.base.init]] are considered to be part of the function body [9.5.1 [dcl.fct.def.general]]. -- end note ]
  2. Change in 9.5.1 [] paragraph 1 as follows: Any informal reference to the body of a function should be interpreted as a reference to the non-terminal function-body, including default member initializers or default initialization used to initialize a base or member subobject in the absence of a mem-initializer-id [11.9.3 [class.base.init]].

2778. Trivial destructor does not imply constant destruction

Section: 7.7 [expr.const] Status: open Submitter: Jiang An Date: 2023-07-27

Consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

04

In C++20, Foo and Bar are literal types, and

1 is well-formed. In C++23, the requirement for the variable b changed to "constant destruction". However, Bar does not have constant destruction, because its mutable member violates 7.7 [] bullet 5.17:

... an invocation of a destructor [11.4.7 [class.dtor]] or a function call whose postfix-expression names a pseudo-destructor [7.6.1.3 [expr.call]], in either case for an object whose lifetime did not begin within the evaluation of E;
  • ...

This is because the mutable member is considered to have its lifetime begin outside of E per 7.7 [] paragraph 9:

An object a is said to have constant destruction if: it is not of class type nor [possibly multidimensional] array thereof, or it is of class type or [possibly multidimensional] array thereof, that class type has a constexpr destructor, and for a hypothetical expression E whose only effect is to destroy a, E would be a core constant expression if the lifetime of a and its non-mutable subobjects [but not its mutable subobjects] were considered to start within E.

Possible resolution:

Change in 7.7 [] paragraph 9 as follows:

An object a is said to have constant destruction if: it is not of class type nor [possibly multidimensional] array thereof, or it is of class type or [possibly multidimensional] array thereof, that class type has a constexpr destructor, any mutable member has a trivial destructor, and for a hypothetical expression E whose only effect is to destroy a, E would be a core constant expression if any mutable members were ignored and if the lifetime of a and its non-mutable subobjects [but not its mutable subobjects] were considered to start within E.

2495. Glvalue result of a function call

Section: 8.7.4 [stmt.return] Status: open Submitter: Jim X Date: 2021-07-04

According to 8.7.4 [] paragraph 1,

A return statement with any other operand shall be used only in a function whose return type is not cv void; the return statement initializes the glvalue result or prvalue result object of the [explicit or implicit] function call by copy-initialization [9.4 [dcl.init]] from the operand.

It is not clear what a “glvalue result” is or what it means to initialize it.

Suggested resolution:

A return statement with any other operand shall be used only in a function whose return type is not cv void;. If the function call is a prvalue, the return statement initializes the glvalue result or prvalue result object of the [explicit or implicit] function call by copy-initialization [9.4 [dcl.init]] from the operand. Otherwise, the return statement is equivalent to the following hypothetical declaration

T t = e;

If the operand of the return statement, X, is a comma expression without parentheses, e is [X], otherwise e is X. T is the return type of the function call; the invented variable t is the result of the function call.

Notes from the August, 2021 teleconference:

A simpler approach would be simply to use a phrase like “returned object or reference” in place of the current wording referring to glvalues and prvalues. This change was regarded as editorial. The issue will remain in "review" status until CWG can look over the wording change.

2123. Omitted constant initialization of local static variables

Section: 8.8 [stmt.dcl] Status: open Submitter: Hubert Tong Date: 2015-02-02

According to 8.8 [] paragraph 4,

The zero-initialization [9.4 [dcl.init]] of all block-scope variables with static storage duration [6.7.5.2 [basic.stc.static]] or thread storage duration [6.7.5.3 [basic.stc.thread]] is performed before any other initialization takes place. Constant initialization [6.9.3.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:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

05

For a related example, consider:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

06

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

2751. Order of destruction for parameters for operator functions

Section: 8.8 [stmt.dcl] Status: open Submitter: Richard Smith Date: 2020-06-10 Liaison: EWG

Subclause 8.8 [] paragraph 2 specifies:

Upon each transfer of control [including sequential execution of statements] within a function from point P to point Q, all variables with automatic storage duration that are active at P and not at Q are destroyed in the reverse order of their construction. ... when a function returns, Q is after its body.

Another description of the same rule is in a note in 8.7.1 [] paragraph 2:

[Note 1: On exit from a scope [however accomplished], objects with automatic storage duration [6.7.5.4 [basic.stc.auto]] that have been constructed in that scope are destroyed in the reverse order of their construction. ... —end note]

However, 12.2.2.3 [] paragraph 2 specifies:

... Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 18 [where @ denotes one of the operators covered in the specified subclause]. However, the operands are sequenced in the order prescribed for the built-in operator [7.6 [expr.compound]].

For operator+= [for example], 7.6.19 [] paragraph 1 requires:

... The right operand is sequenced before the left operand. ...

Now, consider an ABI with callee-cleanup and this example:

static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true static const char [&r] [] = "x"; static const char *s = "x"; static std::initializer_list il = { 'x' }; const bool b2 = r != il.begin[]; // unspecified result const bool b3 = r != s; // unspecified result const bool b4 = il.begin[] != &test1; // always true const bool b5 = r != &test1; // always true

07

In A[]

Chủ Đề