Date: | 2025-09-12
Project: | Programming Language C++
Reference: | ISO/IEC 14882:2024
Reply to: | Jens Maurer
| jens.maurer@gmx.net

## C++ Standard Core Language Active Issues, Revision 118

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

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

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

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:2024 document and corrected defects in the earlier 2020, 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 http://www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2024, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 700 K Street NW, Suite 600, Washington, DC 20001, USA.

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

### Revision History

Revision 118, 2025-09-12:

Added new issues 3022, 3023, 3024, 3025, 3026, 3027, 3028, 3029, 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, and 3063. Reflected the decisions of the June, 2025 plenary meeting. Reflected the deliberations of the teleconferences up to 2025-06-06.

Revision 117, 2025-04-13:

Added new issues 2971, 2972, 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2983, 2984, 2985, 2986, 2987, 2988, 2989, 2990, 2991, 2992, 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, 3020, and 3021. Reflected the decisions of the February, 2025 plenary meeting. Reflected the deliberations of the teleconferences up to 2025-04-11.

Revision 116, 2024-12-19:

Added new issues 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, 2931, 2932, 2933, 2934, 2935, 2936, 2937, 2938, 2939, 2940, 2941, 2942, 2943, 2944, 2945, 2946, 2947, 2948, 2949, 2950, 2951, 2952, 2953, 2954, 2955, 2956, 2957, 2958, 2959, 2960, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, and 2970. Reflected the decisions of the November, 2024 plenary meeting. Reflected the deliberations of the teleconferences up to 2024-12-13.

Revision 115, 2024-07-20:

Added new issues 2880, 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2889, 2890, 2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, 2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, and 2912. Reflected the decisions of the June, 2024 plenary meeting. Reflected the deliberations of the teleconferences up to 2024-06-14.

Revision 114, 2024-04-18:

Added new issues 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, 2857, 2858, 2859, 2860, 2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2870, 2871, 2872, 2873, 2874, 2875, 2876, 2877, 2878, and 2879. Reflected the decisions of the March, 2024 plenary meeting. Reflected the deliberations of the teleconferences up to 2024-04-05.

Revision 113, 2023-12-19:

Added new issues 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, 2800, 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, and 2841. Reflected the decisions of the November, 2023 plenary meeting. Reflected the deliberations of the teleconferences up to 2023-12-15.

Revision 112, 2023-08-18:

Added new issues 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, and 2780. Closed issues 6, 504, 528, and 640. 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 2655, 2656, 2657, 2658, 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, and 2710. 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 578. Added new issues 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2641, Created issues for the handling of NB comments on the Committee Draft: 2639, 2640, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654. 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 2187, 2188, 2189, 2190, 2193, 2195, 2196, 2197, 2200, 2203, 2204, 2211, 2212, 2216, 2221, 2225, 2228, 2230, 2231, 2236, 2245, 2246, 2247, 2248, 2250, 2251, 2258, 2263, 2268, 2269, 2270, 2272, 2274, 2275, 2276, 2279, 2283, 2284, 2286, 2288, 2311, 2314, 2319, and 2320. Closed issue 1326 and 2420 as duplicates. Closed issues 476, 687, 689, 728, 916, 944, 1326, 1393, 1469, 1561, 1742, 1912, 1931, 1972, 2212, and 2341. Dissolved the "concurrency" status in favor of liaison indications, affecting issues 1842 and 2298. Reopened issues 2169 and 2355 after obtaining EWG guidance. Added new issues 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2587, 2588, 2589, 2591, 2592, 2593, 2594, 2595, 2596, 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, and 2609.

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 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, 2527, 2528, 2529, and 2530.

Revision 107, 2021-12-09:

Updated the status of issues 2482 and 2493. Reflected the deliberations of the December, 2021 CWG teleconference. Added new issues 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, 2516, and 2517.

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 2502, 2503, 2504, and 2505.

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 2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, and 2501.

Revision 104, 2021-02-24:

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

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 156, 192, 278, 297, 555, 560, 617, 670, 1230, 1285, 1331, 1426, 1451, 1452, 1545, 1640, 1644, 1646, 1713, 1801, 1857, 1859, 1880, 1917, 1943, 1974, 2045, 2080, 2112, 2121, 2215, 2295, 2343, 2364, 2367, 2371, and 2411. Reflected the deliberations of the February, 2021 teleconference. Added new issues 2470, 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, and 2479.

Revision 102, 2020-12-15: Changed issue 2331 back to "drafting" status after problems were found with the proposed resolution. Fixed transcription errors in the proposed resolution for issue 2020. 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 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, and 2469.

Revision 101, 2019-02-27: Reflected actions at committee meetings and teleconferences. Added new issues 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, and 2402,.

Revision 100, 2018-04-11: Reflected actions at the March, 2018 committee meeting and the April, 2018 teleconference. Added new issues 2357, 2358, 2359, 2360, 2361, 2362, 2363, 2364, and 2365.

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 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, and 2356.

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 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, and 2277. (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 issue 2047 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 issue 1893 back to "drafting" status; it was incorrectly inadvertently accepted as a DR at the November, 2014 meeting.XS Issue 1992 was moved back to drafting to allow application to additional text. Added new issues 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, and 2186.

Revision 94, 2015-05-25: Reflected deliberations of the May, 2015 meeting. Moved issues 1734 and 1928 to "extension" status, reflecting EWG's request to develop a position on trivial special functions before CWG addresses them. Added new issues 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, and 2122.

Revision 93, 2015-04-13: Incorporated EWG decisions on issues 92, 203, 622, 822, 1077, 1259, 1272, 1564, 1577, 1582, 1586, 1643, 1754, 1798, 1826, and 1833. Moved issue 1657 to "accepted" status, reflecting adoption of paper N4266 at the Urbana meeting. Moved issue 1683 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 1299, 1651, 1893, and 1817 to "drafting" status, 1584 to "open" status, and 314, 343, 1710, 1794, and 1812 to "review" status in light of concerns that were raised with their proposed resolutions. Moved issue 1048 to "CD3" status to reflect the fact that it had been addressed by paper N3638, adopted at the April, 2013 (Bristol) meeting. Closed issues 550 and 1855 as duplicates. Reflected the actions of the November, 2014 (Urbana) meeting. Added new issues 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, and 2035.

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

Revision 90, 2014-07-07: Issue 1715 was returned to "drafting" status in light of an alternative suggestion for its resolution. Issue 1927 was closed as a duplicate of issue 1695. Reflected the deliberations of CWG at the 2014-06 (Rapperswil) meeting. Added new issues 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, and 1946.

Revision 89, 2014-05-27: Issues 1351 1356 1465, 1590, 1639 1708, and 1810 were returned to "review" status for further discussion. Restored issue 1397 to "ready"; it had incorrectly been moved back to "drafting" because of a misunderstood comment. Added new issues 1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, and 1931.

- 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 issue 344 to "CD3" when it was noticed that it was resolved by the resolution of issue 1435. Changed the status of issue 1491 to "CD3" when it was noticed that it was resolved by the resolution of issue 1402. Reflected the deliberations of the February, 2014 (Issaquah) meeting. Added new issues 1837, 1838, 1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, and 1865.
- Revision 87, 2014-01-20: Incorporated the results of the drafting review teleconferences held 2013-11-25 and 2014-01-13. Changed status of issue 1469 to "extension", reflecting the consensus of the 2012-08-13 drafting review teleconference. Moved issue 1480 to "WP" status because it was rendered moot by the adoption of N3652 at the April, 2013 meeting. Added new issues 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, and 1836.
- Revision 86, 2013-10-14: Reflected the deliberations of the September, 2013 (Chicago) meeting. Added new issues 1743, 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, and 1795.
- 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 issue 583 to "DR" because it was (silently) addressed by N3624. Changed the status of issue 1531 to "DR" because it had been inadvertently overlooked in recording the motions of the April, 2013 meeting. Changed the status of issue 1597 to "accepted" because it is moot after the adoption of N3652. Returned issue 1609 to "open" status in light of additional discussion. Added new information to the description of issue 1335. Added new issues 1657, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, and 1742.
- 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 issue 1568 to "dup" status. Changed the status of issue 1492 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 1356 and 1465 were incorrectly moved as defect reports and have been returned to "review" status awaiting the adoption of issue 1351. Also, issue 1531 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 1374 and 1543 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 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, and 1656.
- Revision 83, 2013-03-18: Incorporated the results of the drafting review teleconference held 2013-02-04, including moving issue 1566 to "NAD" status. Moved issues 1417 and 1460 back to "review" status in light of new questions. Updated the “more information” link to refer to `isocpp.org`. Added new issues 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, and 1641.
- Revision 82, 2013-01-14: Incorporated the results of the drafting review teleconference held 2012-12-10. Moved issues 903, 1213, 1358, 1492, 1508, and 1552 to "review" status to allow further consideration in light of subsequent discussion. Added new issues 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, and 1605.
- Revision 81, 2012-11-03: Reflected the deliberations of the October, 2012 (Portland) meeting. Added new issues 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, and 1568.
- Revision 80, 2012-09-24: Added remaining descriptions omitted from issues added in revision 77. Corrected status of issues 915, 1313, 1399, and 1437. Changed the status of issues 616, 903, 1318, 1358, and 1402 in light of additional discussion. Incorporated the results of the drafting review teleconference held 2012-08-13. Moved issue 321 to "dup" status. Moved issue 591 to "open" status after the discussion in "dup" issue 1526. Added additional examples to issue 203. Added new issues 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, and 1559.
- Revision 79, 2012-02-27: Reflected deliberations at the February, 2012 (Kona) meeting. Added descriptions omitted from some issues added in revision 77. Moved issue 1427 to "NAD" status. Moved issue 1377 to "dup" status (with respect to issue 1279). Moved issue 1422 to "dup" status (with respect to issue 912). Added a new status, "concurrency", to track issues that will be considered and resolved by the Concurrency Working Group. Added new issues 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, and 1466.
- Revision 78, 2012-01-17: Reflected the results of the December 5, 2011 teleconference. Changed the status of issues 496, 675, 739, 1268, 1269, 1287, and 1312 from "ready" to "review" in response to further discussion of the proposed resolutions. Added new issues 1370, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, and 1443.
- Revision 77, 2011-09-06: Reflected the deliberations of the August, 2011 (Bloomington) meeting. Added new issues 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366, 1367, 1368, and 1369. (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 issue 109 in light of recent discussion. Moved issue 1028 back to "open" status after further analysis. Moved issues 1109 and 1127 to "DR" status, as they had been overlooked in the results of the November, 2010 (Batavia) meeting. Added new issues 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, and 1246.
- Revision 74, 2010-11-29: Reflected deliberations at the November, 2010 (Batavia) meeting. Added new issues 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, and 1226.
- Revision 73, 2010-10-18: Moved issues 994 and 1058 back to "open" status for potential reconsideration. Added analysis to issue 1038. Moved issues 1125 and 1127 back to "drafting" status after further discussion revealed problems with the proposed resolution approved at the August, 2010 meeting. Moved issue 1167 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 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, and 1210,
- Revision 72, 2010-08-23: Reflected deliberations at the August, 2010 (Rapperswil) meeting. Added new issues 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, and 1173.
- Revision 71, 2010-08-07: Added new issues 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, and 1090.
- 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 341, 355, 472, and 529 to "drafting" status for more work. Changed issue 979 to "CD2" status, which was overlooked in the results of the March, 2010 meeting. Closed issues 1041, 1019, and 507 as duplicates of issues 924, 314, and 260, 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 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, and 1068.
- 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 999 and 1000.
- Revision 67, 2009-11-08: Reflected deliberations at the October, 2009 (Santa Cruz, CA) meeting. Added new issues 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, and 998.
- Revision 66, 2009-09-29: Incorporated a new status, "tentatively ready", for issues whose resolutions were reviewed and approved by teleconference between meetings. Changed issue 378 to "CD1" status, as it was resolved by the resolution of issue 276. Added new issues 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, and 971.
- 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 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, and 948.
- Revision 64, 2009-06-19: Fixed incorrect cross-references in issues 867 and 868. Added proposed resolutions for issues 527, 587, 589, 604, 719, 732, and 770 and moved them to "review" status. Added discussion to issues 721, 760, 802, and 872. Added new issues 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, and 921.
- Revision 63, 2009-05-06: Closed issue 839 as a duplicate of issue 803. Added notes to the discussion of issues 325, 626, 667 690, 739, and 841. Moved issue 662 back to "open" status with additional discussion. Added new issues 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, and 888.
- 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 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, and 842.
- Revision 61, 2009-02-08: Provided a reference to a paper containing a proposed resolution for issues 695 and 699 and moved them to "review" status. Added new issues 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, and 766.
- Revision 60, 2008-12-09: Revised the resolution of issue 653 and moved to "review" status. Added new issues 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, and 748.
- 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 696, 704, and 705 and moved them to "review" status. Changed issue 265 to "dup" status in favor of issue 353, which was approved in 2003. Added new issues 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, and 723.
- Revision 58, 2008-08-25: Fixed some incorrect section references. Fixed the title of issue 692. Changed the status of issue 603 to "WP", as the paper that resolved it was approved in April, 2007. Moved issue 657 back to "review" status and added proposed wording. Added discussion to issues 693 and 697. Added or revised resolutions for issues 606, 614, 652, 685, and 694 and moved them to "review" status. Added new issues 704, 705, 706, 707, 708, and 709.
- Revision 57, 2008-07-27: Updated the status of issues 222, 309, and 632 to reflect actions at the June, 2008 meeting that were inadvertently omitted in the preceding revision. Added proposed wording for issue 683 and moved it to "review" status. Added new issues 697, 698, 699, 700, 701, 702, and 703.
- Revision 56, 2008-06-29: Reflected deliberations from the Sophia Antipolis (June, 2008) meeting. Added discussion to issues 507 and 527. Added proposed resolution for issue 653. Added new issues 695 and 696.
- Revision 55, 2008-05-18: Added proposed resolutions for issues 220, 257, 292, 341, 539, 554, 570, 571, 576, 597, 621, 624, 626, 633, 634, 639, and 642, and moved them to "review" status. Moved issue 155 to "dup" status in favor of issue 632, which was changed to "review" status in light of its connection with the initializer-list proposal. Moved issues 530 and 551 to "WP" status because they were resolved by the constexpr proposal. Moved issue 646 to "review" status because it appears to be "NAD". Changed issue 240 to "review" status (from "ready") in light of input from WG14 regarding a similar issue. Added new issues 685, 686, 687, 688, 689, 690, 691, 692, 693, and 694.
- 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 issue 646 to "open" status (it was previously erroneously in "drafting" status). Moved issues 347, 512, and 236 to "review" status with a recommendation to close them as "NAD." Changed issues 220 and 256 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 6 and 150 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 107, 168, 229, 294, and 359 as "NAD" to reflect the recommendation of EWG. Added new issues 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, and 684.
- Revision 53, 2008-02-03: Updated the proposed resolutions for issues 288 and 342. Updated the status of issues 199 and 430 to reflect actions at the Oxford (April, 2007) meeting. Added proposed resolutions and changed the status to "review" for issues 28, 111, 118, 141, 240, 276, 309, 355, 373, 378, 462, 485, 535, 574, 601, 608, 641, 645, and 651. Added new issues 667, 668, 669, 670, 671, and 672.
- Revision 52, 2007-12-09: Updated the status of issues 568 and 620 to reflect actions at the Toronto (July, 2007) meeting. Fixed a typographical error in the example for issue 606. Added new issues 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, and 666.
- Revision 51, 2007-10-09: Reflected deliberations from the Kona (October, 2007) meeting. Added new issues 652 and 653.
- 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 issue 475 with discussion regarding the point at which `std::uncaught_exception()` becomes `false`. Added new issues 642, 643, 644, 645, 646, 647, 648, 649, 650, and 651.
- Revision 49, 2007-08-05: Reflected deliberations from the Toronto (July, 2007) meeting. Added additional discussion to issues 219 and 339. Added new issues 637, 638, 639, 640, and 641.
- Revision 48, 2007-06-24: Added new issues 632, 633, 634, 635, and 636.
- Revision 47, 2007-05-06: Reflected deliberations from the Oxford (April, 2007) meeting. Added new issues 626, 627, 628, 629, 630, and 631.
- Revision 46, 2007-03-11: Added proposed wording to issue 495 and moved it to “review” status. Added new issues 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, and 625.
- Revision 45, 2007-01-14: Changed the status of issue 78 from TC1 to WP. Added new issues 603, 604, 605, 606, 607, 608, 609, 610, and 611.
- Revision 44, 2006-11-05: Reflected deliberations from the Portland (October, 2006) meeting. Added proposed wording for issues 288, 342, and 572, and moved them to “review” status. Added new issues 596, 597, 598, 599, 600, 601, and 602.
- Revision 43, 2006-09-09: Updated issues 218, 357, and 537 with additional discussion and proposed resolutions and moved them to “review” status; added issues 589, 590, 591, 592, 593, 594, and 595.
- Revision 42, 2006-06-23: Updated issues 408 and 561 with additional discussion; added a reference to a paper on the topic to issue 567; and added issues 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, and 588.
- Revision 41, 2006-04-22: Reflected deliberations from the Berlin (April, 2006) meeting. Added issues 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, and 577.
- Revision 40, 2006-02-24: Updated issue 540 to refer to paper J16/06-0022 = WG21 N1952 for its resolution. Updated issue 453 to note the need for additional drafting. Reopened issue 504 and broadened its scope to include object declarations as well. Updated issue 150 (in “extension” status) with additional commentary. Added issues 552, 553, 554, 555, 556, 557, 558, 559, 560, and 561.
- Revision 39, 2005-12-16: Updated issue 488 with additional discussion. Added issues 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, and 551.
- Revision 38, 2005-10-22: Reflected deliberations from the Mont Tremblant (October, 2005) meeting. Added isues 530, 531, 532, 533, 534, 535, 536, and 537.
- Revision 37, 2005-08-27: Added issues 523, 524, 525, 526, 527, 528, and 529.
- Revision 36, 2005-06-27: Reopened issue 484 for additional discussion. Added issues 517, 518, 519, 520, 521, and 522.
- Revision 35, 2005-05-01: Reflected deliberations from the Lillehammer (April, 2005) meeting. Updated issues 189 and 459 with additional discussion. Added new issues 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, and 516.
- Revision 34: 2005-03-06: Closed issue 471 as NAD; updated issues 58, 232, 339, 407, and 494 with additional discussion; and added new issues 498, 499, 500, 501, 502, and 503.
- Revision 33: 2005-01-14: Updated issue 36 with additional discussion. Added new issues 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, and 497.
- Revision 32: 2004-11-07: Reflected deliberations from the Redmond (October, 2004) meeting. Added new issues 475, 476, 477, 478, 479, 480, 481, 482, 483, and 484.
- Revision 31: 2004-09-10: Updated issue 268 with comments from WG14; added comments and changed the status of issue 451 back to “open”; added discussion to issues 334, 341, 385, 399, and 430; and added new issues 470, 471, 472, 473, and 474.
- Revision 30: 2004-04-09: Reflected deliberations from the Sydney (March, 2004) meeting. Added issues 461-469, updated issues 39, 86, 257, 291, 391, 389, 435, 436, 437, 439, 441, 442, 446, 450, 453, and 458.
- Revision 29: 2004-02-13: Added issues 441-460, updated issues 39, 291, 306, 319, 389, 394, 413, and 417.
- 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 issues 54, 301, 372, 382, 391, and 399.
- 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 issue 214.
- 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 issue 298 and issue 214.
- 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 132, 214, 244, 245, 254, 255, 283.
- Revision 20: 2001-11-09: Reflected deliberations from the Redmond (October, 2001) meeting. Added issue 313.
- Revision 19: 2001-09-12: Added new issues 289-308, updated issues 222, 261, 270.
- 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 issue 138 to discuss the interaction of using-declarations and "forward declarations." Noted a problem with the proposed resolution of issue 139. Added some new discussion to issue 115. Added proposed resolution for issue 160. 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 issue 78 into new issue 253; added new issues 254-264.
- Revision 14: 2000-10-21: Added issues 246-252; added an extra question to issue 221 and changed its status back to "review."
- Revision 13: 2000-09-16: Added issues 229-245; changed status of issue 106 to "review" because of problem detected in proposal; added wording for issues 87 and 216 and moved to "review" status; updated discussion of issues 5, 78, 198, 203, and 222.
- 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 62, 73, 89, 94, 106, 121, 134, 142, and 145 to "review" status. Moved issue 13 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 84 and 87. Added proposed wording and moved issues 1, 69, 85, 98, 105, 113, 132, and 178 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 90 and 24; updated issue 89 to include a related question; added issues 169, 170, 171, 172, and 173.
- Revision 7, 1999-09-14: Removed unneeded change to 13.9.4 [temp.expl.spec] paragraph 9 from issue 24; changed issue 85 to refer to 6.5.6 [basic.lookup.elab]; added issues 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, and 168.
- Revision 6, 1999-05-31: Moved issue 72 to "dup" status; added proposed wording and moved issue 90 to "review" status; updated issue 98 with additional question; added issues 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, and 121.
- 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:2024.

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

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

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

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

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

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

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

### Overview

Section | Issue | Status | Liaison | Title
3 intro.defs | 783 | open |  | Definition of “argument”
3 intro.defs | 2632 | drafting |  | 'user-declared' is not defined
4.1 intro.compliance | 949 | open |  | Requirements for freestanding implementations
4.1.1 intro.compliance.general | 2776 | open | EWG | Substitution failure and implementation limits
5.3.1 lex.charset | 2779 | open |  | Restrictions on the ordinary literal encoding
5.5 lex.pptoken | 369 | open |  | Are `new`/`delete` identifiers or preprocessing-op-or-punc?
5.5 lex.pptoken | 1655 | open |  | Line endings in raw string literals
5.6 lex.header | 3019 | open |  | Restrictions on character sequences in header-names
5.8 lex.operators | 189 | open |  | Definition of operator and punctuator
5.9 lex.digraph | 2726 | review | EWG | Alternative tokens appearing as attribute-tokens
5.10 lex.token | 1901 | open |  | punctuator referenced but not defined
5.13 lex.literal | 1924 | review | editor | Definition of “literal” and kinds of literals
5.13.4 lex.fcon | 2752 | open | EWG | Excess-precision floating-point literals
5.13.5 lex.string | 2766 | open |  | Repeated evaluation of a string-literal may yield different objects
5.13.9 lex.ext | 1266 | open |  | user-defined-integer-literal overflow
5.13.9 lex.ext | 1723 | open |  | Multicharacter user-defined character literals
5.13.9 lex.ext | 1735 | open |  | Out-of-range literals in user-defined-literals
6.1 basic.pre | 1529 | drafting |  | Nomenclature for variable vs reference non-static data member
6.1 basic.pre | 2983 | open |  | Non-type template parameters are not variables
6.1 basic.pre | 2992 | open |  | Labels do not have names
6.3 basic.def.odr | 1209 | open |  | Is a potentially-evaluated expression in a template definition a “use?”
6.3 basic.def.odr | 1897 | review |  | ODR vs alternative tokens
6.3 basic.def.odr | 2781 | open |  | Unclear recursion in the one-definition rule
6.3 basic.def.odr | 2782 | open |  | Treatment of closure types in the one-definition rule
6.4 basic.scope | 2969 | open |  | Scopes in the function-try-block of a constructor
6.4.1 basic.scope.scope | 2488 | open |  | Overloading virtual functions and functions with trailing requires-clauses
6.4.1 basic.scope.scope | 2788 | open |  | Correspondence and redeclarations
6.4.1 basic.scope.scope | 2835 | open |  | Name-independent declarations
6.4.1 basic.scope.scope | 3005 | open |  | Function parameters should never be name-independent
6.4.3 basic.scope.block | 2838 | open |  | Declaration conflicts in lambda-expressions
6.4.3 basic.scope.block | 3045 | open |  | Regularizing environment interactions of expansion statement
6.4.6 basic.scope.namespace | 3033 | open |  | Scope after declarator-id before determining correspondence
6.4.9 basic.scope.temp | 2965 | open |  | Generic lambdas do not have a template parameter scope
6.5.1 basic.lookup.general | 2480 | drafting |  | Lookup for enumerators in modules
6.5.1 basic.lookup.general | 2968 | open |  | Name lookup result for typedef-name vs. class-name
6.5.1 basic.lookup.general | 3058 | open |  | "Program point" is not defined
6.5.2 class.member.lookup | 380 | open |  | Definition of "ambiguous base class" missing
6.5.4 basic.lookup.argdep | 2888 | review |  | Missing cases for reference and array types for argument-dependent lookup
6.5.5.1 basic.lookup.qual.general | 1089 | open |  | Template parameters in member selections
6.5.5.1 basic.lookup.qual.general | 2926 | drafting |  | Lookup context for dependent qualified names
6.5.5.1 basic.lookup.qual.general | 2956 | open |  | Missing allowance for pseudo-destructors in qualified lookup
6.7 basic.link | 2670 | open |  | Programs and translation units
6.7 basic.link | 2706 | open |  | Repeated structured binding declarations
6.7 basic.link | 2938 | open |  | Inheriting linkage from a previous declaration
6.7 basic.link | 2945 | open |  | Redundant constraints on matching function template declarations
6.7 basic.link | 2988 | open |  | Is a closure type from a lambda-expression appearing in a concept-definition a TU-local entity?
6.8.2 intro.object | 2324 | drafting |  | Size of base class subobject
6.8.2 intro.object | 2325 | drafting |  | `std::launder` and reuse of character buffers
6.8.2 intro.object | 2334 | open |  | Creation of objects by `typeid`
6.8.2 intro.object | 2469 | drafting |  | Implicit object creation vs constant expressions
6.8.2 intro.object | 2744 | open |  | Multiple objects of the same type at the same address
6.8.2 intro.object | 2765 | open |  | Address comparisons between potentially non-unique objects during constant evaluation
6.8.2 intro.object | 2940 | review |  | Definition of "object"
6.8.2 intro.object | 3039 | open |  | Undefined behavior from implicit object creation ignores observable checkpoints
6.8.3 basic.align | 1211 | open |  | Misaligned lvalues
6.8.3 basic.align | 2840 | open |  | Missing requirements for fundamental alignments
6.8.3 basic.align | 3029 | drafting |  | Confusing note about ordinary character types for aligned memory areas
6.8.4 basic.life | 419 | open |  | Can cast to virtual base class be done on partially-constructed object?
6.8.4 basic.life | 1027 | review |  | Type consistency and reallocation of scalar types
6.8.4 basic.life | 1530 | drafting |  | Member access in out-of-lifetime objects
6.8.4 basic.life | 2258 | open |  | Storage deallocation during period of destruction
6.8.4 basic.life | 2514 | open | SG12 | Modifying const subobjects
6.8.4 basic.life | 2551 | review |  | "Refers to allocated storage" has no meaning
6.8.4 basic.life | 2676 | open |  | Replacing a complete object having base subobjects
6.8.4 basic.life | 2677 | review |  | Replacing union subobjects
6.8.4 basic.life | 2821 | review |  | Lifetime, zero-initialization, and dynamic initialization
6.8.4 basic.life | 2863 | drafting |  | Unclear synchronization requirements for object lifetime rules
6.8.4 basic.life | 2952 | open |  | Vacuous initialization for subobjects
6.8.4 basic.life | 2960 | open |  | Introduce discontiguous object lifetime
6.8.4 basic.life | 2986 | open |  | Creating objects within a mutable member of a const object
6.8.4 basic.life | 3001 | open |  | Inconsistent restrictions for `static_cast` on pointers to out-of-lifetime objects
6.8.4 basic.life | 3047 | open |  | Calling destructors on out-of-lifetime objects
6.8.6 basic.stc | 365 | open |  | Storage duration and temporaries
6.8.6 basic.stc | 1634 | open |  | Temporary storage duration
6.8.6.5.2 basic.stc.dynamic.allocation | 1676 | drafting |  | `auto` return type for allocation and deallocation functions
6.8.6.5.2 basic.stc.dynamic.allocation | 1682 | open |  | Overly-restrictive rules on function templates as allocation functions
6.8.6.5.2 basic.stc.dynamic.allocation | 2073 | open |  | Allocating memory for exception objects
6.8.6.5.3 basic.stc.dynamic.deallocation | 523 | open |  | Can a one-past-the-end pointer be invalidated by deleting an adjacent object?
6.8.6.5.3 basic.stc.dynamic.deallocation | 2042 | drafting |  | Exceptions and deallocation functions
6.8.6.5.3 basic.stc.dynamic.deallocation | 3025 | open |  | Deallocation functions returning void
6.8.7 class.temporary | 2434 | review |  | Mandatory copy elision vs non-class objects
6.8.7 class.temporary | 2666 | open |  | Lifetime extension through `static_cast`
6.8.7 class.temporary | 2826 | drafting |  | Missing definition of "temporary expression"
6.8.7 class.temporary | 2832 | open |  | Invented temporary variables and temporary objects
6.8.7 class.temporary | 2868 | open |  | Self-references in trivially copyable objects as function return values
6.8.7 class.temporary | 2941 | open |  | Lifetime extension for function-style cast to reference type
6.8.7 class.temporary | 3043 | open |  | Lifetime extension for temporaries in expansion statements
6.8.7 class.temporary | 3063 | open |  | Lifetime extension of temporaries past function return
6.9 basic.types | 350 | open | WG14 | `signed char` underlying representation for objects
6.9 basic.types | 1701 | open |  | Array vs sequence in object representation
6.9.1 basic.types.general | 2953 | open |  | Value representation for non-trivially-copyable types
6.9.2 basic.fundamental | 146 | open |  | Floating-point zero
6.9.2 basic.fundamental | 251 | open |  | How many signed integer types are there?
6.9.2 basic.fundamental | 2827 | review |  | Representation of unsigned integral types
6.9.2 basic.fundamental | 2966 | open |  | Alignment and value representation of `std::nullptr_t`
6.9.3 basic.extended.fp | 3036 | open |  | Extended floating-point types should not be cv-qualified
6.9.4 basic.compound | 2544 | open |  | Address of past-the-end of a potentially-overlapping subobject
6.10.1 intro.execution | 698 | open |  | The definition of “sequenced before” is too narrow
6.10.1 intro.execution | 2955 | open |  | Unify rules about conflicting unordered accesses
6.10.2 intro.multithread | 1842 | open | SG1 | Unevaluated operands and “carries a dependency”
6.10.2.2 intro.races | 2297 | open |  | Unclear specification of atomic operations
6.10.2.2 intro.races | 2298 | open | SG1 | Actions and expression evaluation
6.10.2.2 intro.races | 2587 | review |  | Visible side effects and initial value of an object
6.10.2.3 intro.progress | 2816 | review |  | Unclear phrasing "may assume ... eventually"
6.10.2.3 intro.progress | 2923 | review | SG1 | Note about infinite loops and execution steps
6.10.3.1 basic.start.main | 3060 | open |  | Change in behavior for `noexcept` `main`
6.10.3.2 basic.start.static | 371 | open |  | Interleaving of constructor calls
6.10.3.2 basic.start.static | 1294 | open |  | Side effects in dynamic/static initialization
6.10.3.2 basic.start.static | 1659 | open |  | Initialization order of thread_local template static data members
6.10.3.2 basic.start.static | 1986 | drafting |  | odr-use and delayed initialization
6.10.3.2 basic.start.static | 2148 | drafting |  | Thread storage duration and order of initialization
6.10.3.2 basic.start.static | 2914 | review |  | Unclear order of initialization of static and thread-local variables
6.10.3.3 basic.start.dynamic | 2444 | drafting |  | Constant expressions in initialization odr-use
6.10.3.3 basic.start.dynamic | 2684 | open |  | thread_local dynamic initialization
6.10.3.3 basic.start.dynamic | 2833 | review |  | Evaluation of odr-use
6.10.3.3 basic.start.dynamic | 2928 | open |  | No ordering for initializing thread-local variables
6.10.3.4 basic.start.term | 2929 | review |  | Lifetime of trivially-destructible static or thread-local objects
7.2.1 basic.lval | 3042 | open |  | Implicit object creation is insufficient to model effective type rule of C
7.3.2 conv.lval | 2964 | open |  | Reading "invalid pointer values"
7.3.6 conv.qual | 2438 | open |  | Problems in the specification of qualification conversions
7.4 expr.arith.conv | 2981 | open |  | Usual arithmetic conversions and result types
7.5.4 expr.prim.paren | 2989 | open |  | Remove misleading general allowance for parentheses
7.5.5 expr.prim.id | 2503 | drafting |  | Unclear relationship among name, qualified name, and unqualified name
7.5.5.1 expr.prim.id.general | 2902 | review |  | Implicit `this` transformation outside of permitted contexts
7.5.5.2 expr.prim.id.unqual | 2738 | review |  | "denotes a destructor" is missing specification
7.5.5.3 expr.prim.id.qual | 2972 | open |  | Declarative nested-name-specifier naming a partial specialization
7.5.5.5 expr.prim.id.dtor | 2473 | open |  | Parentheses in pseudo-destructor calls
7.5.6.3 expr.prim.lambda.capture | 2086 | drafting |  | Reference odr-use vs implicit capture
7.5.6.3 expr.prim.lambda.capture | 2737 | review |  | Temporary lifetime extension for reference init-captures
7.5.8.1 expr.prim.req.general | 2565 | open | EWG | Invalid types in the parameter-declaration-clause of a requires-expression
7.5.8.3 expr.prim.req.type | 3056 | open |  | Missing semicolons in grammar for type-requirement
7.5.8.5 expr.prim.req.nested | 2739 | open |  | Nested requirement not a constant expression
7.6.1.3 expr.call | 2284 | open |  | Sequencing of braced-init-list arguments
7.6.1.3 expr.call | 2515 | open |  | Result of a function call
7.6.1.3 expr.call | 2660 | open |  | Confusing term "this parameter"
7.6.1.3 expr.call | 2688 | open |  | Calling explicit object member functions
7.6.1.3 expr.call | 3054 | open |  | Use of default arguments depending on shape of postfix-expression in a function call
7.6.1.4 expr.type.conv | 914 | open | EWG | Value-initialization of array types
7.6.1.5 expr.ref | 2557 | review |  | Class member access referring to an unrelated class
7.6.1.5 expr.ref | 2705 | open |  | Accessing ambiguous subobjects
7.6.1.5 expr.ref | 2957 | open |  | Evaluating a reference member should constitute access
7.6.1.5 expr.ref | 2959 | open |  | Naming enumerators in class member access expressions
7.6.1.6 expr.post.incr | 742 | open |  | Postfix increment/decrement with long bit-field operands
7.6.1.8 expr.typeid | 282 | open |  | Namespace for `extended_type_info`
7.6.1.9 expr.static.cast | 2048 | open |  | C-style casts that cast away constness vs `static_cast`
7.6.1.9 expr.static.cast | 2243 | drafting |  | Incorrect use of implicit conversion sequence
7.6.2.2 expr.unary.op | 3026 | open |  | Class for pointer-to-member formation
7.6.2.5 expr.sizeof | 2609 | open |  | Padding in class types
7.6.2.5 expr.sizeof | 2817 | open |  | sizeof(abstract class) is underspecified
7.6.2.8 expr.new | 267 | open |  | Alignment requirement for new-expressions
7.6.2.8 expr.new | 901 | open |  | Deleted `operator delete`
7.6.2.8 expr.new | 1628 | open |  | Deallocation function templates
7.6.2.8 expr.new | 2281 | drafting |  | Consistency of aligned `operator delete` replacement
7.6.2.8 expr.new | 2532 | open |  | Kind of pointer value returned by `new T[0]`
7.6.2.8 expr.new | 2566 | review |  | Matching deallocation for uncaught exception
7.6.2.8 expr.new | 2592 | open |  | Missing definition for placement allocation/deallocation function
7.6.2.8 expr.new | 2623 | drafting |  | Invoking destroying `operator delete` for constructor failure
7.6.2.8 expr.new | 2812 | open |  | Allocation with explicit alignment
7.6.2.8 expr.new | 2912 | open |  | Too-large value for size in array new
7.6.2.8 expr.new | 3011 | open |  | Parenthesized aggregate initialization for new-expressions
7.6.2.9 expr.delete | 196 | open |  | Arguments to deallocation functions
7.6.2.9 expr.delete | 2805 | open |  | Underspecified selection of deallocation function
7.6.2.9 expr.delete | 2889 | open |  | Requiring an accessible destructor for destroying operator delete
7.6.3 expr.cast | 2878 | open | EWG | C-style casts to reference types
7.6.4 expr.mptr.oper | 2593 | review |  | Insufficient base class restriction for pointer-to-member expression
7.6.6 expr.add | 2013 | drafting |  | Pointer subtraction in large array
7.6.6 expr.add | 2182 | drafting |  | Pointer arithmetic in array-like containers
7.6.10 expr.eq | 2786 | open |  | Comparing pointers to complete objects
7.6.16 expr.cond | 2023 | drafting |  | Composite reference result type of conditional operator
7.6.16 expr.cond | 2316 | drafting |  | Simplifying class conversions in conditional expressions
7.6.16 expr.cond | 3000 | open |  | Handling of cv-qualified class types in conditional operator
7.6.19 expr.assign | 1542 | open |  | Compound assignment of braced-init-list
7.7 expr.const | 1255 | drafting |  | Definition problems with `constexpr` functions
7.7 expr.const | 1256 | open |  | Unevaluated operands are not necessarily constant expressions
7.7 expr.const | 2166 | drafting |  | Unclear meaning of “undefined `constexpr` function”
7.7 expr.const | 2192 | open |  | Constant expressions and order-of-eval undefined behavior
7.7 expr.const | 2301 | open |  | Value-initialization and constexpr constructor evaluation
7.7 expr.const | 2456 | open |  | Viable user-defined conversions in converted constant expressions
7.7 expr.const | 2536 | open | EWG | Partially initialized variables during constant initialization
7.7 expr.const | 2545 | open |  | Transparently replacing objects in constant expressions
7.7 expr.const | 2559 | open |  | Defaulted consteval functions
7.7 expr.const | 2633 | open |  | typeid of constexpr-unknown dynamic type
7.7 expr.const | 2656 | drafting |  | Converting consteval lambda to function pointer in non-immediate context
7.7 expr.const | 2702 | open |  | Constant destruction of reference members
7.7 expr.const | 2734 | open |  | Immediate forward-declared function templates
7.7 expr.const | 2740 | open |  | Too many objects have constexpr-unknown type
7.7 expr.const | 2778 | review |  | Trivial destructor does not imply constant destruction
7.7 expr.const | 2800 | review |  | Instantiating constexpr variables for potential constant evaluation
7.7 expr.const | 2962 | open |  | Evaluation of destructor call for variable with constant destruction
7.7 expr.const | 3004 | open |  | Pointer arithmetic on array of unknown bound
7.7 expr.const | 3009 | open |  | Unclear rules for constant initialization
7.7 expr.const | 3010 | open |  | constexpr placement-new should require transparent replaceability
7.7 expr.const | 3059 | open |  | `throw;` in constant expressions
8.6.5 stmt.ranged | 1680 | drafting |  | Including `<initializer_list>` for range-based `for`
8.7 stmt.expand | 3044 | open |  | Iterating expansion statements woes
8.7 stmt.expand | 3048 | open |  | Empty destructuring expansion statements
8.7 stmt.expand | 3061 | open |  | Trailing comma in an expansion-init-list
8.8 stmt.jump | 2115 | open |  | Order of implicit destruction vs release of automatic storage
8.8.4 stmt.return | 2495 | open |  | Glvalue result of a function call
8.8.4 stmt.return | 2995 | open |  | Meaning of flowing off the end of a function
8.8.4 stmt.return | 3052 | open |  | Unclear handling of checks on discarded `return` statements
8.10 stmt.dcl | 2123 | open |  | Omitted constant initialization of local static variables
8.10 stmt.dcl | 2976 | review |  | Transferring control out of a function
8.11 stmt.ambig | 2963 | open |  | Paradoxical variable-or-function declaration
9.1 dcl.pre | 157 | open |  | Omitted typedef declarator
9.2.2 dcl.stc | 498 | open |  | Storage class specifiers in definitions of class members
9.2.2 dcl.stc | 2232 | open |  | `thread_local` anonymous unions
9.2.4 dcl.typedef | 2973 | open |  | Does an alias-declaration introduce a name for linkage purposes?
9.2.6 dcl.constexpr | 3012 | open |  | Deviating `constexpr` or `consteval` across translation units
9.2.9.2 dcl.type.cv | 2195 | open |  | Unsolicited reading of trailing volatile members
9.2.9.3 dcl.type.simple | 3003 | open |  | Naming a deducible template for class template argument deduction
9.2.9.5 dcl.type.elab | 144 | open |  | Position of `friend` specifier
9.2.9.7 dcl.spec.auto | 1348 | drafting |  | Use of `auto` in a trailing-return-type
9.2.9.7 dcl.spec.auto | 1670 | review | EWG | `auto` as conversion-type-id
9.2.9.7 dcl.spec.auto | 1868 | open |  | Meaning of “placeholder type”
9.2.9.7 dcl.spec.auto | 2412 | review |  | SFINAE vs undeduced placeholder type
9.3.2 dcl.name | 1488 | drafting |  | abstract-pack-declarators in type-ids
9.3.3 dcl.ambig.res | 2228 | review | EWG | Ambiguity resolution for cast to function type
9.3.4.1 dcl.meaning.general | 2671 | open |  | friend named by a template-id
9.3.4.5 dcl.array | 3030 | open |  | Initializing array prvalues of unknown bound
9.3.4.6 dcl.fct | 1001 | review |  | Parameter type adjustment in dependent parameter types
9.3.4.6 dcl.fct | 1668 | drafting |  | Parameter type determination still not clear enough
9.3.4.6 dcl.fct | 1790 | open | EWG | Ellipsis following function parameter pack
9.3.4.6 dcl.fct | 2537 | drafting |  | Overbroad grammar for parameter-declaration
9.3.4.6 dcl.fct | 2553 | review |  | Restrictions on explicit object member functions
9.3.4.6 dcl.fct | 2802 | open |  | Constrained `auto` and redeclaration with non-abbreviated syntax
9.3.4.6 dcl.fct | 2942 | open |  | Packs in a function's parameter-type-list
9.3.4.7 dcl.fct.default | 325 | open |  | When are default arguments parsed?
9.3.4.7 dcl.fct.default | 361 | open |  | Forward reference to default argument
9.3.4.7 dcl.fct.default | 1580 | drafting |  | Default arguments in explicit instantiations
9.3.4.7 dcl.fct.default | 1609 | open |  | Default arguments and function parameter packs
9.3.4.7 dcl.fct.default | 2701 | open |  | Default arguments in multiple scopes / inheritance of array bounds in the same scope
9.3.4.7 dcl.fct.default | 3062 | open |  | Overlapping specification of default template arguments
9.5 dcl.init | 2327 | drafting |  | Copy elision for direct-initialization with a conversion function
9.5.1 dcl.init.general | 2977 | review |  | Initialization with string literals
9.5.1 dcl.init.general | 2991 | open |  | "array size" is vague
9.5.2 dcl.init.aggr | 2128 | open |  | Imprecise rule for reference member initializer
9.5.3 dcl.init.string | 1304 | drafting |  | Omitted array bound with string initialization
9.5.4 dcl.init.ref | 1414 | drafting |  | Binding an rvalue reference to a reference-unrelated lvalue
9.5.4 dcl.init.ref | 1827 | drafting |  | Reference binding with ambiguous conversions
9.5.4 dcl.init.ref | 2704 | open |  | Clarify meaning of "bind directly"
9.5.5 dcl.init.list | 1996 | drafting |  | Reference list-initialization ignores conversion functions
9.5.5 dcl.init.list | 2168 | open |  | Narrowing conversions and +/- infinity
9.5.5 dcl.init.list | 2742 | drafting |  | Guaranteed copy elision for brace-initialization from prvalue
9.5.5 dcl.init.list | 3023 | open |  | Default arguments in list-initialization
9.6.1 dcl.fct.def.general | 1962 | open | EWG | Type of `__func__`
9.6.1 dcl.fct.def.general | 2362 | open | EWG | `__func__` should be `constexpr`
9.6.1 dcl.fct.def.general | 2993 | open |  | Body of a destructor
9.6.2 dcl.fct.def.default | 1854 | drafting |  | Disallowing use of implicitly-deleted functions
9.6.2 dcl.fct.def.default | 2997 | open |  | Defaulted functions with deleted definition
9.6.4 dcl.fct.def.coroutine | 2562 | open |  | Exceptions thrown during coroutine startup
9.6.4 dcl.fct.def.coroutine | 2563 | review | EWG | Initialization of coroutine result object
9.6.4 dcl.fct.def.coroutine | 2934 | open |  | Unclear semantics of exception escaping from `unhandled_exception`
9.6.4 dcl.fct.def.coroutine | 2935 | open |  | Destroying the coroutine state when initial-await-resume-called is false
9.6.4 dcl.fct.def.coroutine | 3040 | open |  | Mishandling of lambda coroutines
9.7 dcl.struct.bind | 2340 | open |  | Reference collapsing and structured bindings
9.8.1 dcl.enum | 1485 | drafting |  | Out-of-class definition of member unscoped opaque enumeration
9.8.1 dcl.enum | 2131 | drafting |  | Ambiguity with opaque-enum-declaration
9.8.1 dcl.enum | 2932 | review | EWG | Value range of empty enumeration
9.8.1 dcl.enum | 3046 | open |  | Enumerations as part of the common initial sequence
9.9.2.2 namespace.unnamed | 2505 | drafting |  | Nested unnamed namespace of inline unnamed namespace
9.10 namespace.udecl | 813 | open |  | `typename` in a using-declaration with a non-dependent name
9.10 namespace.udecl | 2555 | drafting |  | Ineffective redeclaration prevention for using-declarators
9.10 namespace.udecl | 3028 | open |  | A using-declarator should bind a name
9.10 namespace.udecl | 3037 | open |  | Name lookup results for using-declarators
9.12 dcl.link | 1817 | open |  | Linkage specifications and nested scopes
9.13 dcl.attr | 2866 | open | EWG | Observing the effects of `[[no_unique_address]]`
9.13.1 dcl.attr.grammar | 1706 | drafting |  | `alignas` pack expansion syntax
9.13.1 dcl.attr.grammar | 3038 | open | EWG | Ignorability of attributes, again
9.13.2 dcl.align | 1617 | open |  | `alignas` and non-defining declarations
9.13.2 dcl.align | 2223 | drafting |  | Multiple `alignas` specifiers
9.13.2 dcl.align | 3024 | open |  | Alignment of references
9.13.4 dcl.attr.deprecated | 3050 | open |  | [[deprecated]] for class template partial specializations
10.1 module.unit | 2541 | open |  | Linkage specifications, module purview, and module attachment
10.2 module.interface | 2607 | drafting |  | Visibility of enumerator names
10.3 module.import | 2727 | open |  | Importing header units synthesized from source files
10.4 module.global.frag | 2971 | open |  | Specializations for a class are not decl-reachable
11.2 class.prop | 511 | open |  | POD-structs with template assignment operators
11.2 class.prop | 2463 | open | EWG | Trivial copyability and unions with non-trivial members
11.2 class.prop | 2736 | open |  | Standard layout class with empty base class also in first member
11.2 class.prop | 3049 | open | EWG | Implicitly deleted move operation should not disable trivial relocation
11.4 class.mem | 1890 | drafting |  | Member type depending on definition of member function
11.4.1 class.mem.general | 2188 | open |  | empty-declaration grammar ambiguity
11.4.1 class.mem.general | 2852 | open |  | Complete-class contexts and class-scope lambdas
11.4.1 class.mem.general | 2979 | open |  | Duplicate declarations of enumerations in class scope
11.4.1 class.mem.general | 3051 | open |  | Missing specification for types of member subobjects
11.4.4 special | 2787 | open |  | Kind of explicit object copy/move assignment function
11.4.5 class.ctor | 1623 | drafting |  | Deleted default union constructor and member initializers
11.4.5 class.ctor | 1808 | drafting |  | Constructor templates vs default constructors
11.4.5.1 class.ctor.general | 2841 | open |  | When do const objects start being const?
11.4.5.2 class.default.ctor | 2799 | drafting |  | Inheriting default constructors
11.4.5.2 class.default.ctor | 2885 | review | LWG | Non-eligible trivial default constructors
11.4.5.2 class.default.ctor | 2999 | open | EWG | Trivial unions changing existing behavior
11.4.5.3 class.copy.ctor | 1092 | drafting |  | Cycles in overload resolution during instantiation
11.4.5.3 class.copy.ctor | 1548 | open |  | Copy/move construction and conversion functions
11.4.5.3 class.copy.ctor | 1594 | drafting |  | Lazy declaration of special members vs overload errors
11.4.5.3 class.copy.ctor | 2203 | drafting |  | Defaulted copy/move constructors and UDCs
11.4.5.3 class.copy.ctor | 2264 | drafting |  | Memberwise copying with indeterminate value
11.4.5.3 class.copy.ctor | 2743 | open |  | Copying non-trivial objects nested within a union
11.4.5.3 class.copy.ctor | 2837 | open |  | Instantiating and inheriting by-value copy constructors
11.4.6 class.copy.assign | 2329 | open |  | Virtual base classes and generated assignment operators
11.4.6 class.copy.assign | 2897 | open |  | Copying potentially-overlapping union subobjects
11.4.7 class.dtor | 1977 | open |  | Contradictory results of failed destructor lookup
11.4.7 class.dtor | 2158 | drafting |  | Polymorphic behavior during destruction
11.4.7 class.dtor | 2839 | open |  | Explicit destruction of base classes
11.4.7 class.dtor | 3022 | review |  | Redundant specification of explicit destructor calls
11.4.7 class.dtor | 3041 | open |  | Overly aggressive rule for deleting the destructor of a union
11.4.8.3 class.conv.fct | 2513 | open |  | Ambiguity with requires-clause and operator-function-id
11.4.9.3 class.static.data | 1283 | open |  | Static data members of classes with typedef name for linkage purposes
11.4.9.3 class.static.data | 1721 | review |  | Diagnosing ODR violations for static data members
11.4.9.3 class.static.data | 2335 | drafting |  | Deduced return types vs member types
11.4.10 class.bit | 2950 | open |  | Value preservation in enumeration vs. integer bit-fields
11.5 class.union | 57 | open |  | Empty unions
11.5 class.union | 1404 | open |  | Object reallocation in unions
11.5 class.union | 1702 | drafting |  | Rephrasing the definition of “anonymous union”
11.5.1 class.union.general | 2675 | open |  | `start_lifetime_as`, placement-new, and active union members
11.5.2 class.union.anon | 2767 | open |  | Non-defining declarations of anonymous unions
11.5.2 class.union.anon | 2773 | open |  | Naming anonymous union members as class members
11.5.2 class.union.anon | 3035 | open |  | Lambda expressions in anonymous unions
11.7.3 class.virtual | 2554 | review |  | Overriding virtual functions, also with explicit object parameters
11.8.3 class.access.base | 2246 | drafting |  | Access of indirect virtual base class constructors
11.8.4 class.friend | 1699 | open | EWG | Does befriending a class befriend its friends?
11.8.5 class.protected | 472 | open |  | Casting across protected inheritance
11.8.5 class.protected | 1883 | review |  | Protected access to constructors in mem-initializers
11.8.5 class.protected | 2187 | drafting |  | Protected members and access via qualified-id
11.8.5 class.protected | 2244 | open |  | Base class access in aggregate initialization
11.9 class.init | 2756 | review |  | Completion of initialization by delegating constructor
11.9.3 class.base.init | 1915 | open | EWG | Potentially-invoked destructors in non-throwing constructors
11.9.3 class.base.init | 2056 | open |  | Member function calls in partially-initialized class objects
11.9.3 class.base.init | 2403 | drafting |  | Temporary materialization and base/member initialization
11.9.3 class.base.init | 2669 | open | EWG | Lifetime extension for aggregate initialization
11.9.5 class.cdtor | 1517 | open |  | Unclear/missing description of behavior during construction/destruction
11.9.5 class.cdtor | 2757 | review |  | Deleting or deallocating storage of an object during its construction
11.9.6 class.copy.elision | 1049 | open |  | Copy elision through reference parameters of inline functions
11.10.1 class.compare.default | 3007 | open | EWG | Access checking during synthesis of defaulted comparison operator, take 2
12.2.2.1 over.match.funcs.general | 3031 | open |  | Finding declarations for conversion operators for access checking
12.2.2.2.2 over.call.func | 1278 | drafting |  | Incorrect treatment of contrived object
12.2.2.2.3 over.call.object | 2189 | open |  | Surrogate call template
12.2.2.2.3 over.call.object | 2564 | drafting |  | Conversion to function pointer with an explicit object parameter
12.2.2.2.3 over.call.object | 3055 | open |  | Misleading body for surrogate call function
12.2.2.3 over.match.oper | 545 | open |  | User-defined conversions and built-in operator overload resolution
12.2.2.3 over.match.oper | 1919 | open |  | Overload resolution for `!` with explicit conversion operator
12.2.2.3 over.match.oper | 2089 | drafting |  | Restricting selection of builtin overloaded operators
12.2.2.3 over.match.oper | 2730 | open |  | Comparison templates on enumeration types
12.2.2.3 over.match.oper | 2797 | review | EWG | Meaning of "corresponds" for rewritten operator candidates
12.2.2.3 over.match.oper | 2804 | open | EWG | Lookup for determining rewrite targets
12.2.2.3 over.match.oper | 2844 | open |  | Enumerating a finite set of built-in candidates
12.2.2.7 over.match.ref | 2028 | drafting |  | Converting constructors in rvalue reference initialization
12.2.2.7 over.match.ref | 2108 | drafting |  | Conversions to non-class prvalues in reference initialization
12.2.2.7 over.match.ref | 2967 | open |  | Explicit conversion functions
12.2.2.8 over.match.list | 2194 | drafting |  | Impossible case in list initialization
12.2.2.8 over.match.list | 2311 | open |  | Missed case for guaranteed copy elision
12.2.2.9 over.match.class.deduct | 2425 | open |  | Confusing wording for deduction from a type
12.2.2.9 over.match.class.deduct | 2467 | drafting |  | CTAD for alias templates and the deducible check
12.2.2.9 over.match.class.deduct | 2471 | drafting |  | Nested class template argument deduction
12.2.2.9 over.match.class.deduct | 2680 | open |  | Class template argument deduction for aggregates with designated initializers
12.2.4 over.match.best | 2735 | open |  | List-initialization and conversions in overload resolution
12.2.4.2 over.best.ics | 2319 | drafting |  | Nested brace initialization from same type
12.2.4.2.1 over.best.ics.general | 2525 | open |  | Incorrect definition of implicit conversion sequence
12.2.4.2.1 over.best.ics.general | 2679 | open |  | Implicit conversion sequence with a null pointer constant
12.2.4.2.1 over.best.ics.general | 2829 | open |  | Redundant case in restricting user-defined conversion sequences
12.2.4.2.3 over.ics.user | 2731 | open |  | List-initialization sequence with a user-defined conversion
12.2.4.2.5 over.ics.ref | 2077 | drafting |  | Overload resolution and invalid rvalue-reference initialization
12.2.4.2.5 over.ics.ref | 3057 | open |  | Ranking of derived-to-base conversions should ignore reference binding
12.2.4.2.6 over.ics.list | 1536 | drafting |  | Overload resolution with temporary from initializer list
12.2.4.2.6 over.ics.list | 2169 | open |  | Narrowing conversions and overload resolution
12.2.4.2.6 over.ics.list | 2492 | open |  | Comparing user-defined conversion sequences in list-initialization
12.2.4.2.6 over.ics.list | 2741 | open |  | Implicit conversion sequence from empty list to array of unknown bound
12.2.4.2.6 over.ics.list | 2790 | open |  | Aggregate initialization and user-defined conversion sequence
12.2.4.3 over.ics.rank | 1459 | open |  | Reference-binding tiebreakers in overload resolution
12.2.4.3 over.ics.rank | 1789 | open |  | Array reference vs array decay in overload resolution
12.2.4.3 over.ics.rank | 2110 | drafting |  | Overload resolution for base class conversion and reference/non-reference
12.2.4.3 over.ics.rank | 2337 | open |  | Incorrect implication of logic ladder for conversion sequence tiebreakers
12.2.4.3 over.ics.rank | 2842 | open |  | Preferring an `initializer_list` over a single value
12.2.4.3 over.ics.rank | 2958 | open |  | Overload resolution involving lvalue transformation and qualification conversion
12.3 over.over | 2572 | review |  | Address of overloaded function with no target
12.3 over.over | 2873 | open |  | Taking the address of a function involving template argument deduction
12.4 over.oper | 1989 | drafting |  | Insufficient restrictions on parameters of postfix operators
12.4.3 over.binary | 1549 | open |  | Overloaded comma operator with `void` operand
12.5 over.built | 260 | open |  | User-defined conversions and built-in `operator=`
12.5 over.built | 954 | open |  | Overload resolution of conversion operator templates with built-in types
12.6 over.literal | 1620 | open |  | User-defined literals and extended integer types
13 temp | 205 | drafting |  | Templates and static data members
13.1 temp.pre | 1463 | drafting | EWG | `extern "C"` alias templates
13.1 temp.pre | 2862 | review |  | Unclear boundaries of template declarations
13.1 temp.pre | 2904 | open |  | Introducing template-names
13.1 temp.pre | 2917 | review | EWG | Disallow multiple friend-type-specifiers for a friend template
13.2 temp.param | 1444 | drafting |  | Type adjustments of non-type template parameters
13.2 temp.param | 1635 | drafting |  | How similar are template default arguments to function default arguments?
13.2 temp.param | 2395 | drafting |  | Parameters following a pack expansion
13.2 temp.param | 2617 | review |  | Default template arguments for template members of non-template classes
13.2 temp.param | 2994 | open |  | Allowing template parameters following template parameter packs that are pack expansions
13.3 temp.names | 579 | open |  | What is a “nested” `>` or `>>`?
13.3 temp.names | 2903 | drafting |  | Can we omit the `template` disambiguator in nested-name-specifiers in type-only contexts?
13.3 temp.names | 2920 | open |  | The `template` keyword for base classes
13.3 temp.names | 2980 | open |  | Constraints on template template parameters
13.4 temp.arg | 2105 | open |  | When do the arguments for a parameter pack end?
13.4.1 temp.arg.general | 3032 | open |  | Template argument disambiguation
13.4.3 temp.arg.nontype | 2043 | drafting |  | Generalized template arguments and array-to-pointer decay
13.4.3 temp.arg.nontype | 2401 | drafting |  | Array decay vs prohibition of subobject non-type arguments
13.4.4 temp.arg.template | 2057 | drafting |  | Template template arguments with default arguments
13.4.4 temp.arg.template | 2398 | drafting |  | Template template parameter matching and deduction
13.5 temp.constr | 2961 | open |  | Checking of ill-formed types in constraint-expressions
13.5.2 temp.constr.constr | 2686 | open |  | Pack expansion into a non-pack parameter of a concept
13.5.2.3 temp.constr.atomic | 2589 | review |  | Context of access checks during constraint satisfaction checking
13.5.2.3 temp.constr.atomic | 2996 | open |  | Impenetrable definition of atomic constraint
13.5.4 temp.constr.normal | 2975 | open |  | Effect of concept template-head on parameter mappings
13.5.5 temp.constr.order | 3021 | open |  | Subsumption rules for fold expanded constraints
13.6 temp.type | 2037 | drafting |  | Alias templates and template declaration matching
13.6 temp.type | 3027 | open |  | Equivalence of pack-index-specifiers
13.7 temp.decls | 1730 | drafting |  | Can a variable template have an unnamed type?
13.7.1 temp.decls.general | 2951 | open |  | Distinguishing a primary template
13.7.4 temp.variadic | 1432 | open |  | Newly-ambiguous variadic template expansions
13.7.5 temp.friend | 2118 | open |  | Stateful metaprogramming via friend injection
13.7.6 temp.spec.partial | 708 | open |  | Partial specialization of member templates of class templates
13.7.6 temp.spec.partial | 1647 | drafting |  | Type agreement of non-type template arguments in partial specializations
13.7.6 temp.spec.partial | 2127 | drafting |  | Partial specialization and nullptr
13.7.6 temp.spec.partial | 2173 | open |  | Partial specialization with non-deduced contexts
13.7.6 temp.spec.partial | 2916 | review |  | Variable template partial specializations should not be declared `static`
13.7.6.1 temp.spec.partial.general | 2179 | drafting |  | Required diagnostic for partial specialization after first use
13.7.6.1 temp.spec.partial.general | 2948 | open |  | Late ambiguity for partial template specialization
13.7.6.2 temp.spec.partial.match | 549 | drafting |  | Non-deducible parameters in partial specializations
13.7.6.4 temp.spec.partial.member | 1755 | drafting |  | Out-of-class partial specializations of member templates
13.7.7.2 temp.over.link | 310 | open |  | Can function templates differing only in parameter cv-qualifiers be overloaded?
13.7.7.2 temp.over.link | 2584 | open |  | Equivalent types in function template declarations
13.7.7.2 temp.over.link | 2946 | open |  | Dependent call equivalence in non-ADL cases
13.7.7.3 temp.func.order | 402 | open |  | More on partial ordering of function templates
13.7.7.3 temp.func.order | 1157 | open |  | Partial ordering of function templates is still underspecified
13.7.7.3 temp.func.order | 2160 | open |  | Issues with partial ordering
13.7.7.3 temp.func.order | 2834 | review |  | Partial ordering and explicit object parameters
13.7.7.3 temp.func.order | 2949 | open |  | Treatment of ellipsis during partial ordering
13.7.8 temp.alias | 1286 | open |  | Equivalence of alias templates
13.7.8 temp.alias | 1430 | open |  | Pack expansion into fixed alias template parameter list
13.7.8 temp.alias | 1554 | drafting |  | Access and alias templates
13.7.8 temp.alias | 1979 | drafting |  | Alias template specialization in template member definition
13.7.8 temp.alias | 1980 | drafting |  | Equivalent but not functionally-equivalent redeclarations
13.7.8 temp.alias | 2236 | drafting |  | When is an alias template specialization dependent?
13.7.8 temp.alias | 2794 | open |  | Uniqueness of lambdas in alias templates
13.8 temp.res | 1257 | open |  | Instantiation via non-dependent references in uninstantiated templates
13.8 temp.res | 2067 | open |  | Generated variadic templates requiring empty pack
13.8.1 temp.res.general | 2462 | open |  | Problems with the omission of the `typename` keyword
13.8.1 temp.res.general | 2468 | open |  | Omission of the `typename` keyword in a member template parameter list
13.8.2 temp.local | 186 | open |  | Name hiding and template template-parameters
13.8.3.2 temp.dep.type | 1390 | drafting |  | Dependency of alias template specializations
13.8.3.2 temp.dep.type | 1524 | drafting |  | Incompletely-defined class template base
13.8.3.2 temp.dep.type | 1619 | open |  | Definition of current instantiation
13.8.3.2 temp.dep.type | 2074 | drafting |  | Type-dependence of local class of function template
13.8.3.3 temp.dep.expr | 2275 | drafting |  | Type-dependence of function template
13.8.3.3 temp.dep.expr | 2487 | drafting |  | Type dependence of function-style cast to incomplete array type
13.8.3.4 temp.dep.constexpr | 2774 | open |  | Value-dependence of requires-expressions
13.8.3.4 temp.dep.constexpr | 2984 | open |  | Value-dependent structured bindings
13.8.3.7 temp.dep.temp | 2090 | open |  | Dependency via non-dependent base class
13.8.3.7 temp.dep.temp | 3002 | open |  | Template parameter/argument confusion
13.8.4 temp.dep.res | 2 | drafting |  | How can dependent names be used in member declarations that appear outside of the class template definition?
13.8.4.1 temp.point | 287 | drafting |  | Order dependencies in template instantiation
13.8.4.1 temp.point | 1845 | drafting |  | Point of instantiation of a variable template specialization
13.8.4.1 temp.point | 2245 | drafting |  | Point of instantiation of incomplete class template
13.8.4.1 temp.point | 2250 | open |  | Implicit instantiation, destruction, and TUs
13.8.4.1 temp.point | 2497 | drafting |  | Points of instantiation for constexpr function templates
13.9 temp.spec | 2435 | open |  | Alias template specializations
13.9.2 temp.inst | 1602 | review |  | Linkage of specialization vs linkage of template arguments
13.9.2 temp.inst | 1856 | open |  | Indirect nested classes of class templates
13.9.2 temp.inst | 2202 | drafting |  | When does default argument instantiation occur?
13.9.2 temp.inst | 2222 | drafting |  | Additional contexts where instantiation is not required
13.9.2 temp.inst | 2263 | drafting |  | Default argument instantiation for `friend`s
13.9.2 temp.inst | 2265 | drafting |  | Delayed pack expansion and member redeclarations
13.9.2 temp.inst | 2596 | drafting |  | Instantiation of constrained non-template friends
13.9.2 temp.inst | 2808 | review |  | Explicit specialization of defaulted special member function
13.9.2 temp.inst | 3034 | open |  | Infinite recursion should hit an implementation limit
13.9.3 temp.explicit | 293 | open |  | Syntax of explicit instantiation/specialization too permissive
13.9.3 temp.explicit | 1046 | open |  | What is a “use” of a class specialization?
13.9.3 temp.explicit | 1665 | drafting |  | Declaration matching in explicit instantiations
13.9.3 temp.explicit | 2421 | drafting |  | Explicit instantiation of constrained member functions
13.9.3 temp.explicit | 2501 | drafting |  | Explicit instantiation and trailing requires-clauses
13.9.3 temp.explicit | 3006 | open |  | Vague restrictions for explicit instantiations of class templates
13.9.4 temp.expl.spec | 529 | open |  | Use of `template<>` with “explicitly-specialized” class templates
13.9.4 temp.expl.spec | 1840 | drafting |  | Non-deleted explicit specialization of deleted function template
13.9.4 temp.expl.spec | 1993 | open |  | Use of `template<>` defining member of explicit specialization
13.9.4 temp.expl.spec | 2409 | drafting |  | Explicit specializations of constexpr static data members
13.9.4 temp.expl.spec | 2847 | review |  | Constrained explicit specializations of function templates at class scope
13.10.2 temp.arg.explicit | 264 | open |  | Unusable template constructors and conversion functions
13.10.2 temp.arg.explicit | 2055 | drafting |  | Explicitly-specified non-deduced parameter packs
13.10.3 temp.deduct | 697 | open |  | Deduction rules apply to more than functions
13.10.3 temp.deduct | 1172 | drafting |  | “instantiation-dependent” constructs
13.10.3 temp.deduct | 1322 | drafting |  | Function parameter type decay in templates
13.10.3 temp.deduct | 1582 | drafting |  | Template default arguments and deduction failure
13.10.3 temp.deduct | 1844 | open | EWG | Defining “immediate context”
13.10.3 temp.deduct | 2296 | open | EWG | Are default argument instantiation failures in the “immediate context”?
13.10.3 temp.deduct | 2896 | review |  | Template argument deduction involving exception specifications
13.10.3.1 temp.deduct.general | 2498 | open |  | Partial specialization failure and the immediate context
13.10.3.1 temp.deduct.general | 2769 | open |  | Substitution into template parameters and default template arguments should be interleaved
13.10.3.1 temp.deduct.general | 2770 | open |  | Trailing requires-clause can refer to function parameters before they are substituted into
13.10.3.2 temp.deduct.call | 503 | open |  | Cv-qualified function types in template argument deduction
13.10.3.2 temp.deduct.call | 1513 | drafting |  | `initializer_list` deduction failure
13.10.3.2 temp.deduct.call | 1584 | drafting |  | Deducing function types from cv-qualified types
13.10.3.2 temp.deduct.call | 1939 | open |  | Argument conversions to nondeduced parameter types revisited
13.10.3.2 temp.deduct.call | 2978 | open |  | Deduction involving reference to similar types
13.10.3.3 temp.deduct.funcaddr | 1486 | drafting |  | Base-derived conversion in member pointer deduction
13.10.3.5 temp.deduct.partial | 1221 | open |  | Partial ordering and reference collapsing
13.10.3.5 temp.deduct.partial | 1610 | drafting |  | Cv-qualification in deduction of reference to array
13.10.3.5 temp.deduct.partial | 2998 | open |  | Missing deduction consistency check for partial ordering
13.10.3.6 temp.deduct.type | 1763 | open |  | Length mismatch in template type deduction
13.10.3.6 temp.deduct.type | 2328 | drafting |  | Unclear presentation style of template argument deduction rules
13.10.3.6 temp.deduct.type | 2900 | open |  | Deduction of non-type template arguments with placeholder types
13.10.3.6 temp.deduct.type | 2974 | open |  | Non-deduced context for qualified-id naming a template
14.4 except.handle | 2172 | drafting |  | Multiple exceptions with one exception object
14.4 except.handle | 2219 | drafting |  | Dynamically-unreachable handlers
14.5 except.spec | 2417 | open |  | Explicit instantiation and exception specifications
15.1 cpp.pre | 2002 | open | WG14 | White space within preprocessing directives
15.2 cpp.cond | 925 | open |  | Type of character literals in preprocessor expressions
15.2 cpp.cond | 1436 | open |  | Interaction of constant expression changes with preprocessor expressions
15.2 cpp.cond | 2190 | open |  | Insufficient specification of `__has_include`
15.2 cpp.cond | 2575 | open | SG12 | Undefined behavior when macro-replacing "defined" operator
15.2 cpp.cond | 3017 | open |  | Commas in controlling expression of conditional inclusion
15.3 cpp.include | 2576 | open | SG12 | Undefined behavior with macro-expanded `#include` directives
15.5 cpp.module | 2947 | open |  | Limiting macro expansion in pp-module
15.7 cpp.replace | 1718 | open | WG14 | Macro invocation spanning end-of-file
15.7 cpp.replace | 2003 | drafting |  | Zero-argument macros incorrectly specified
15.7.1 cpp.replace.general | 2577 | open | SG12 | Undefined behavior for preprocessing directives in macro arguments
15.7.1 cpp.replace.general | 3053 | open |  | Allowing `#undef likely`
15.7.3 cpp.stringize | 1625 | open | WG14 | Adding spaces between tokens in stringizing
15.7.3 cpp.stringize | 1709 | open |  | Stringizing raw string literals containing newline
15.7.3 cpp.stringize | 2578 | open | SG12 | Undefined behavior when creating an invalid string literal via stringizing
15.7.4 cpp.concat | 2522 | open | WG14 | Removing placemarker tokens and retention of whitespace
15.7.4 cpp.concat | 2579 | open | SG12 | Undefined behavior when token pasting does not create a preprocessing token
15.7.5 cpp.rescan | 268 | open | WG14 | Macro name suppression in rescanned replacement text
15.8 cpp.line | 2580 | open | SG12 | Undefined behavior with `#line`
15.8 cpp.line | 2693 | open | WG14 | Escape sequences for the string-literal of `#line`
15.10 cpp.pragma | 1889 | open |  | Unclear effect of `#pragma` on conformance
15.12 cpp.predefined | 2581 | open | SG12 | Undefined behavior for predefined macros
15.13 cpp.pragma.op | 897 | open |  | `_Pragma` and extended string-literals
15.13 cpp.pragma.op | 2694 | open | WG14 | string-literals of the `_Pragma` operator
17.2.4 support.types.layout | 2784 | open | EWG | Unclear definition of member-designator for `offsetof`
17.14.3 csetjmp.syn | 2361 | open |  | Unclear description of `longjmp` undefined behavior
Annex C diff | 1944 | open |  | New C incompatibilities
C.6 diff.cpp03 | 1279 | open |  | Additional differences between C++ 2003 and C++ 2011
C.7 diff.iso | 1248 | open |  | Updating Annex C to C99 and C23
C.7.4 diff.expr | 2875 | review |  | Missing support for round-tripping null pointer values through indirection/address operators
C.7.6 diff.dcl | 3008 | open |  | Missing Annex C entry for `void` object declarations

### Issues with "Ready" Status

### Issues with "Tentatively Ready" Status

### Issues with "Review" Status

#### 2726. Alternative tokens appearing as attribute-tokens

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

Subclause 5.9 [lex.digraph] 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.13.1 [dcl.attr.grammar] paragraph 4 specifies:

> ... If a keyword (5.12 [lex.key]) or an alternative token (5.9 [lex.digraph]) that satisfies the syntactic requirements of an identifier (5.11 [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 [lex.phases] paragraph 2 as follows:

   > Whitespace characters separating tokens are no longer significant. Each preprocessing token is converted into a token (5.10 [lex.token]), replacing each alternative token by its corresponding primary token (5.9 [lex.digraph]). The resulting tokens constitute a translation unit and are syntactically and semantically analyzed and translated.

2. Change in 9.13.1 [dcl.attr.grammar] paragraph 4 as follows:

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

This resolution also addresses issue 1897.

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.9 [lex.digraph] paragraph 2,

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

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

CWG 2023-05-12

Addressed by issue 2726.

#### 2888. Missing cases for reference and array types for argument-dependent lookup

Section: 6.5.4 [basic.lookup.argdep] Status: review Submitter: Lewis Baker Date: 2024-04-30

The specification for associated entities in argument-dependent lookup recurses into template argument types, but misses handling reference and array cases. Those cannot appear for function arguments. For example:

      namespace N {
        template<typename T>
        struct A {
          operator T() const noexcept;
        };
      }
      namespace M {
        struct B {};
        void f(B&);
      }

      void caller() {
        N::A<M::B&> arg;
        f(arg);
      }

Possible resolution:

Change in 6.5.4 [basic.lookup.argdep] paragraph 3 as follows:

> For each argument type T in the function call, there is a set of zero or more associated entities to be considered. The set of entities is determined entirely by the types of the function arguments (and any template template arguments). Any typedef-names and using-declarations used to specify the types do not contribute to this set. The set of associated entities for a type cv `T` is determined in the following way:
> - If T is a fundamental type, its associated set of associated entities is empty.
> - If T is a class type (including unions), its associated entities are: the class itself; the class of which it is a member, if any; and, if it is a complete type, its direct and indirect base classes. Furthermore, if T is a class template specialization, its associated entities also include: the entities associated with the types of the template arguments provided for template type parameters; the templates used as template template arguments; and the classes of which any member templates used as template template arguments are members. [Note 2: Non-type template arguments do not contribute to the set of associated entities. —end note]
> - If T is an enumeration type, its associated entities are T and, if it is a class member, the member's class.
> - If T is a pointer to U, a reference to `U`, or an array of U, its associated entities are those associated with U.
> - If T is a function type, its associated entities are those associated with the function parameter types and those associated with the return type.
> - If T is a pointer to a member function of a class X, its associated entities are those associated with the function parameter types and return type, together with those associated with X.
> - If T is a pointer to a data member of class X, its associated entities are those associated with the member type together with those associated with X.
> In addition, if the argument is an overload set or the address of such a set, its associated entities are the union of those associated with each of the members of the set, i.e., the entities associated with its parameter types and return type. Additionally, The associated entities of a function call are the associated entities of each argument A of the call, consisting of
> - if A is an id-expression whose terminal name refers to an overload set O, the union of the associated entities of the function types of the members of O, and
> - if the aforementioned overload set is named with A is a template-id I whose terminal name refers to an overload set, its associated entities also include its the template template-arguments of I and those the associated with its entities of each type template-arguments. template-argument of I, and
> - otherwise, if A has a type, the associated entities of the type of A,
> - otherwise, A has no associated entities.

#### 2940. Definition of "object"

Section: 6.8.2 [intro.object] Status: review Submitter: Roman Jasmann Date: 2024-09-22

Prior to P0137R1, the term "object" was defined to be "a region of storage". Currently, the term "object" appears to be missing a definition. The submitter suggests to restore wording similar to "An object is a region of storage that is occupied or released, associated with an object type and certain properties." and disagrees with the resolutions suggested below.

Possible resolution (option 1) [SUPERSEDED]:

Change in 6.8.2 [intro.object] paragraph 1 as follows:

> An object is a logical element of the execution model of the abstract machine (4.1.2 [intro.abstract]). The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]), by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union (11.5 [class.union]), or when a temporary object is created (7.3.5 [conv.rval], 6.8.7 [class.temporary]). An object occupies a region of storage in its period of construction (11.9.5 [class.cdtor]), throughout its lifetime (6.8.4 [basic.life]), and in its period of destruction (11.9.5 [class.cdtor]). [Note 1: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note]

Possible resolution (option 2) [SUPERSEDED]:

Change in 6.8.2 [intro.object] paragraph 1 as follows:

> An object is an element of the execution representation of the program in the abstract machine (4.1.2 [intro.abstract]) that represents an instance of an object type. The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]), by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union (11.5 [class.union]), or when a temporary object is created (7.3.5 [conv.rval], 6.8.7 [class.temporary]). An object occupies a region of storage in its period of construction (11.9.5 [class.cdtor]), throughout its lifetime (6.8.4 [basic.life]), and in its period of destruction (11.9.5 [class.cdtor]). [Note 1: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note]

CWG 2024-11-08

An alternative phrasing was suggested.

Update 2025-01-13: The submitter of the issue conveys his agreement with this phrasing.

Possible resolution [SUPERSEDED]:

Change in 6.8.2 [intro.object] paragraph 1 as follows:

> An object is a logical unit of data that occupies storage in the abstract machine (4.1.2 [intro.abstract]). The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]), by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union (11.5 [class.union]), or when a temporary object is created (7.3.5 [conv.rval], 6.8.7 [class.temporary]). An object occupies a region of storage in its period of construction (11.9.5 [class.cdtor]), throughout its lifetime (6.8.4 [basic.life]), and in its period of destruction (11.9.5 [class.cdtor]). [Note 1: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note]

Additional notes (January, 2025)

Objects may be of zero sizes; those do not occupy storage at all.

Possible resolution:

Change in 6.8.2 [intro.object] paragraph 1 as follows:

> An object is a logical unit of data that has an address (see below) in the storage of the abstract machine (4.1.2 [intro.abstract]). The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]), by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union (11.5 [class.union]), or when a temporary object is created (7.3.5 [conv.rval], 6.8.7 [class.temporary]). An object occupies a region of storage in its period of construction (11.9.5 [class.cdtor]), throughout its lifetime (6.8.4 [basic.life]), and in its period of destruction (11.9.5 [class.cdtor]). [Note 1: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note]

#### 1027. Type consistency and reallocation of scalar types

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

Is the following well-formed?

        int f() {
            int i = 3;
            new (&i) float(1.2);
            return i;
        }

The wording that is intended to prevent such shenanigans, 6.8.4 [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 1116 and 1338.)

Proposed resolution (August, 2010) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1116.

Possible resolution:

1. Change in 6.8.4 [basic.life] paragraph 6 as follows:

   > Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [ Footnote: ... ], 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. ...

2. Change in 6.8.4 [basic.life] paragraph 7 as follows:

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

#### 2551. "Refers to allocated storage" has no meaning

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

6.8.4 [basic.life] 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.8.6.5.2 [basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type `void*` is well-defined.

Similarly, 6.8.4 [basic.life] 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.8.6.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.8.2 [intro.object].

See also issue 1853.

Proposed resolution:

1. Change in 6.8.4 [basic.life] 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.8.6.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.8.4 [basic.life] 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.8.6.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.8.4 [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:

      union U { int i, j; };
      U u;
      new (&u) U{.i = 5};
      int k = u.j;  // OK! New U::i transparently replaces existing u.j!

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.8.4 [basic.life] 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.8.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 issue 2676.

Suggested resolution:

1. Change in 6.8.2 [intro.object] 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.8.4 [basic.life] 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.8.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.8.2 [intro.object]) for which:
   >   - the complete object of o1 is not const or
   >   - o1 is a mutable member subobject or a subobject thereof.

#### 2821. Lifetime, zero-initialization, and dynamic initialization

Section: 6.8.4 [basic.life] Status: review Submitter: Jan Schultke Date: 2023-07-24

Consider:

      int i = i;

According to 6.10.3.2 [basic.start.static] paragraph 2, `i` is zero-initialized and then dynamic initialization is applied. However, it is unclear whether the lifetime of `i` has started at the point when the dynamic initialization occurs. According to 6.8.4 [basic.life] paragraph 1:

> ... 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.5 [dcl.init]),
> except ...

Possible directions:

- Zero-initialization starts the lifetime, and the dynamic initialization just modifies the value, similar to a delegating constructor.
- The lifetime doesn't begin until the dynamic initialization is complete, but we allow scalar objects to be "under construction" (11.9.5 [class.cdtor]).
- Zero-initialization starts the lifetime, and dynamic initialization transparently replaces the object.

The example in 6.10.3.2 [basic.start.static] paragraph 3 suggests the first option.

CWG 2024-01-19

The first option is is intended.

Possible resolution:

Split and change 6.8.4 [basic.life] paragraph 1 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 any initialization (if any) is complete (including vacuous initialization) (9.5 [dcl.init]),
> except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union (9.5.2 [dcl.init.aggr], 11.9.3 [class.base.init]), or as described in 11.5 [class.union], 11.4.5.3 [class.copy.ctor], and 11.4.6 [class.copy.assign], and except as described in 20.2.10.2 [allocator.members]. [ Note: In particular, the lifetime of a complete class object begins when the non-delegating constructor returns (11.9.3 [class.base.init]). The lifetime of a complete object with static or thread storage duration begins when static initialization is complete (6.10.3.2 [basic.start.static]). -- end note ]

> The lifetime of an object o of type T ends when: ...

#### 2434. Mandatory copy elision vs non-class objects

Section: 6.8.7 [class.temporary] Status: review Submitter: Richard Smith Date: 2019-09-30

In the following example,

      int f() {
        X x;
        return 4;
      }
      int a = f();

`a` must be directly initialized in the return statement of `f()` because the exception permitting temporaries for function arguments and return types in 6.8.7 [class.temporary] paragraph 3 applies only to certain class types:

> When an object of class type `X` is passed to or returned from a function, if `X` has at least one eligible copy or move constructor (11.4.4 [special]), each such constructor is trivial, and the destructor of `X` is either trivial or deleted, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the eligible trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). [Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. —end note]

This requirement is observable, since the destructor of `X` in the example could inspect the value of `a`.

The permissions in this paragraph should also apply to all non-class types.

Possible resolution:

Change in 6.8.7 [class.temporary] paragraph 3 as follows:

> When an object of class type X is passed to or returned from a function, if X has at least one eligible copy or move constructor (11.4.4 [special]), each such constructor is trivial, and the destructor of X is either trivial or deleted, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the eligible trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). Similarly, implementations are permitted to create a temporary object of scalar type to hold a function parameter or result object. [Note 4: This latitude is granted to allow objects of scalar or class type to be passed to or returned from functions in registers. —end note]

#### 2827. Representation of unsigned integral types

Section: 6.9.2 [basic.fundamental] Status: review Submitter: David Detweiler Date: 2021-09-12

(From editorial issue 4893.)

It is unclear whether the representation of unsigned integral types is unspecified or implementation-defined.

Proposed resolution [SUPERSEDED]:

Change in 6.9.2 [basic.fundamental] paragraph 17 as follows:

> The types described in this subclause are called fundamental types. The representation of a fundamental type is unspecified except as stated in this subclause.

CWG 2023-12-01

Since there is no requirement for "implementation-defined" in the specification, the representation is unspecified by omission. It was noted that "unspecified behavior" has well-defined (and narrow) meaning, whereas the not-specified property here is static throughout the instance of the abstract machine. In that sense, it is similar to implementation-defined, abent the documentation requirement. It might be worthwhile to have a defined term for such properties.

Proposed resolution:

Change in 6.9.2 [basic.fundamental] paragraph 17 as follows:

> The types described in this subclause are called fundamental types. The representation of a fundamental type is implementation-defined, subject to the constraints in this subclause.

#### 2587. Visible side effects and initial value of an object

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

Subclause 6.10.2.2 [intro.races] 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.10.1 [intro.execution] 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.10.1 [intro.execution] 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. ...

#### 2816. Unclear phrasing "may assume ... eventually"

Section: 6.10.2.3 [intro.progress] Status: review Submitter: Jiang An Date: 2023-04-26

Subclause 6.10.2.3 [intro.progress] paragraph 1 specifies:

> The implementation may assume that any thread will eventually do one of the following:
> - terminate,
> - make a call to a library I/O function,
> - perform an access through a volatile glvalue, or
> - perform a synchronization operation or an atomic operation.

This formulation has two issues. First, what are the consequences of the user violation the assumption? It should be clearly specified that undefined behavior results. Second, what does "eventually" mean? The wording gives the impression that the requirement is satisfied if a thread performs some I/O operation at its beginning and then goes into an empty infinite loop.

It was also noted that that 6.10.2.3 [intro.progress] paragraph 3 is a near-duplicate of paragraph 1.

Possible resolution:

1. Remove 6.10.2.3 [intro.progress] paragraph 1:

   > The implementation may assume that any thread will eventually do one of the following:
   > - terminate,
   > - make a call to a library I/O function,
   > - perform an access through a volatile glvalue, or
   > - perform a synchronization operation or an atomic operation.
   > [Note 1: This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven. —end note]

2. Change in 6.10.2.3 [intro.progress] paragraph 5 as follows:

   > The behavior is undefined if a thread of execution that has not terminated stops making execution steps. [Note: This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven. Because of this and the preceding requirement regarding what threads of execution have to perform eventually, it It follows that no thread of execution can execute forever without an execution step occurring. —end note]

#### 2923. Note about infinite loops and execution steps

Section: 6.10.2.3 [intro.progress] Status: review Submitter: Simon Cooksey Date: 2024-08-12 Liaison: SG1

Given that certain infinite loops are no longer undefined behavior (see P2809R3 (Trivial infinite loops are not Undefined Behavior), adopted in March 2024), the note in 6.10.2.3 [intro.progress] paragraph 5 claiming there are no executions without eventual execution steps should be struck.

Proposed resolution (approved by CWG 2024-09-13):

Remove 6.10.2.3 [intro.progress] paragraph 5:

> [Note 4: Because of this and the preceding requirement regarding what threads of execution have to perform eventually, it follows that no thread of execution can execute forever without an execution step occurring. —end note]

CWG 2024-11-19

The definition of "execution step" in 6.10.2.3 [intro.progress] paragraph 3 does not include the invocation of `std::this_thread::yield`. However, trivial infinite loops do nothing but yield, and are not undefined behavior. Therefore, the note is believed to be factually wrong. CWG seeks advice from SG1 for resolving the inconsistency, via paper issue #2139.

#### 2914. Unclear order of initialization of static and thread-local variables

Section: 6.10.3.2 [basic.start.static] Status: review Submitter: Brian Bi Date: 2024-06-20

(From submission #559.)

Specification is missing that clarifies that non-local variables with static storage duration are initialized before those with thread storage duration.

Suggested resolution:

1. Change in 6.10.3.2 [basic.start.static] paragraph 1 as follows:

   > Variables with static storage duration are initialized as a consequence of program initiation. Variables with thread storage duration are initialized as a consequence of thread execution. If the program does not start a thread (6.10.2 [intro.multithread]) other than the main thread (6.10.3.1 [basic.start.main]) before the completion of all non-deferred (6.10.3.3 [basic.start.dynamic]) initialization of non-block variables with static storage duration, then all non-deferred initialization (if any) of non-block variables with static storage duration strongly happens before any non-deferred initialization of non-block variables with thread storage duration. Within each of these phases of initiation, initialization occurs as follows.

2. Change in 6.10.3.3 [basic.start.dynamic] paragraph 5 as follows:

   > It is implementation-defined whether the dynamic initialization of a non-block non-inline variable o with static storage duration is sequenced before the first statement of main or is deferred. If it is deferred, it strongly happens before any non-initialization odr-use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized o and any deferred initialiation of non-inline non-block variables with thread storage duration defined in the same translation unit as o. [ Footnote: ... ] It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

#### 2833. Evaluation of odr-use

Section: 6.10.3.3 [basic.start.dynamic] Status: review Submitter: Brian Bi Date: 2023-12-01

The concept of odr-use is a static program property, independent of runtime control flow. For example,

    void f() {
      if (false)
        g();          // unconditionally odr-uses g
    }

Yet, some parts of the standard talk about an odr-use participating in the happens-before relation, which is a category error.

Proposed resolution [SUPERSEDED]:

1. Change in 6.3 [basic.def.odr] paragraph 6 through 8 as follows:

   > A structured binding is odr-used if it appears as by a potentially-evaluated expression E if it is named by E.

   > *this is odr-used if this appears as a potentially-evaluated expression (including as the result of the implicit transformation in the body of a non-static member function (11.4.3 [class.mfct.non.static])).

   > A virtual member function is odr-used if it is not pure. A function is odr-used by if it is named by a potentially-evaluated expression or conversion E if it is named by E. A non-placement allocation or deallocation function for a class is odr-used by the definition of a constructor of that class. A non-placement deallocation function for a class is odr-used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (11.4.7 [class.dtor]). [ Footnote: ... ]

2. Change in 6.10.3.3 [basic.start.dynamic] paragraph 4 as follows:

   > A non-initialization odr-use of an entity is the evaluation of an expression that odr-uses an odr-use (6.3 [basic.def.odr]) the entity where the evaluation is not caused directly or indirectly by the initialization of a non-block static or thread storage duration variable.

CWG 2024-01-19

Since "odr-use" is a category error here, use the term "non-initialization use" instead.

Possible resolution:

1. Change in 6.3 [basic.def.odr] paragraph 6 through 8 as follows:

   > A structured binding is odr-used if it appears as by a potentially-evaluated expression E if it is named by E.

   > *this is odr-used if this appears as a potentially-evaluated expression (including as the result of the implicit transformation in the body of a non-static member function (11.4.3 [class.mfct.non.static])).

   > A virtual member function is odr-used if it is not pure. A function is odr-used by if it is named by a potentially-evaluated expression or conversion E if it is named by E. A non-placement allocation or deallocation function for a class is odr-used by the definition of a constructor of that class. A non-placement deallocation function for a class is odr-used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (11.4.7 [class.dtor]). [ Footnote: ... ]

2. Change in 6.10.3.3 [basic.start.dynamic] paragraph 4 through 7 as follows:

   > A non-initialization odr-use use of an entity is the evaluation of an expression that odr-uses an odr-use (6.3 [basic.def.odr]) the entity where the evaluation is not caused directly or indirectly by the initialization of a non-block static or thread storage duration variable.

   > ...If it is deferred, it strongly happens before any non-initialization odr-use use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized. [ Footnote: ... ] ... [ Example: ... It is implementation-defined whether either a or b is initialized before main is entered or whether the initializations are delayed until a is first odr-used used in main. In particular, if a is initialized before main is entered, it is not guaranteed that b will be initialized before it is odr-used used by the initialization of a, that is, before A::A is called. If, however, a is initialized at some point after the first statement of main, b will be initialized prior to its use in A::A. -- end example ]

   > ... If it is deferred, it strongly happens before any non-initialization odr-use use of that variable. ...

   > ... If it is deferred, the initialization associated with the entity for thread t is sequenced before the first non-initialization odr-use use by t of any non-inline variable with thread storage duration ...

#### 2929. Lifetime of trivially-destructible static or thread-local objects

Section: 6.10.3.4 [basic.start.term] Status: review Submitter: Mathias Stearn Date: 2024-08-05

Issue 2256 (adopted in February, 2019) changed the rules such that the lifetimes of objects do not depend on whether the destructor is trivial or not. However, the motivation of that issue mentioned only objects with automatic storage duration. To be able to avoid order-of-destruction issues across translation units, it would be useful to never end the lifetime of trivially-destructible objects with static storage duration and to delay the end of the lifetime of such objects with thread storage duration as long as possible.

Possible resolution [SUPERSEDED]:

1. Change in 6.10.3.4 [basic.start.term] paragraph 1 as follows:

   > Constructed class objects (9.5 [dcl.init]) with static storage duration and having a non-trivial destructor (11.4.7 [class.dtor]) are destroyed and functions registered with std::atexit are called as part of a call to std::exit (17.5 [support.start.term]). The call to std::exit is sequenced before the destructions and the registered functions.

2. Change in 6.10.3.4 [basic.start.term] paragraph 2 as follows:

   > Constructed class objects with thread storage duration within a given thread and having a non-trivial destructor (11.4.7 [class.dtor]) are destroyed as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The destruction of all those constructed objects with thread storage duration within that thread strongly happens before releasing the storage for objects with thread storage duration within that thread, which in turn strongly happens before destroying any object with static storage duration.

CWG 2024-09-27

Also allow for constant destruction, consider arrays of class type, and adjust the happens-before requirements.

Proposed resolution (approved by CWG 2024-10-11) [SUPERSEDED]:

1. Change in 6.10.3.4 [basic.start.term] paragraph 1 as follows:

   > Constructed complete objects (9.5 [dcl.init]) with static storage duration and not having constant destruction (7.7 [expr.const]) are destroyed and functions registered with std::atexit are called as part of a call to std::exit (17.5 [support.start.term]). The call to std::exit is sequenced before the destructions and the registered functions.

2. Change in 6.10.3.4 [basic.start.term] paragraph 2 as follows:

   > Constructed complete objects with thread storage duration within a given thread and not having constant destruction (7.7 [expr.const]) are destroyed as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The destruction of those constructed objects within that thread is sequenced before releasing the storage for all objects with thread storage duration within that thread (6.8.6.3 [basic.stc.thread]). Also, the The destruction of all those constructed objects with thread storage duration within that thread strongly happens before destroying any object with static storage duration.

CWG 2024-11-22

Continue to actually destroy constant-destruction objects, but do so after all dynamic destruction is done.

Proposed resolution:

1. Change in 6.10.3.4 [basic.start.term] paragraph 1 as follows:

   > Constructed complete objects (9.5 [dcl.init]) with static storage duration are destroyed and functions registered with std::atexit are called as part of a call to std::exit (17.5 [support.start.term]). The call to std::exit is sequenced before the destructions and the registered functions.

2. Change in 6.10.3.4 [basic.start.term] paragraph 2 as follows:

   > Constructed complete objects with thread storage duration within a given thread are destroyed as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The destruction of those constructed objects is sequenced before releasing the storage for any object with thread storage duration within that thread (6.8.6.3 [basic.stc.thread]). The The destruction of all those constructed objects with thread storage duration within that thread strongly happens before destroying any object with static storage duration.

3. Add a paragraph after 6.10.3.4 [basic.start.term] paragraph 2:

   > The destruction of an object with thread storage duration within a given thread and having constant destruction (7.7 [expr.const]) is sequenced after the destruction of any other object with thread storage duration within the thread. The destruction of an object with static storage duration and having constant destruction is sequenced after the destruction of any other object with static storage duration and after any call to a function passed to `std::atexit`. The sequencing rules in the remainder of this subclause apply only to objects not having constant destruction.

4. Do not change in 6.10.3.4 [basic.start.term] paragraph 3:

   > If the completion of the constructor or dynamic initialization of an object with static storage duration strongly happens before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If the completion of the constructor or dynamic initialization of an object with thread storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any block variable with static storage duration initialized during the construction of the subobjects is destroyed. If the destruction of an object with static or thread storage duration exits via an exception, the function std::terminate is called (14.6.2 [except.terminate]).

#### 2902. Implicit `this` transformation outside of permitted contexts

Section: 7.5.5.1 [expr.prim.id.general] Status: review Submitter: Vincent X Date: 2024-06-14

(From submission #553.)

Subclause 7.5.5.1 [expr.prim.id.general] paragraph 2 specifies:

> If an id-expression E denotes a non-static non-type member of some class C at a point where the current class (7.5.3 [expr.prim.this]) is X and
> - E is potentially evaluated or C is X or a base class of X, and
> - E is not the id-expression of a class member access expression (7.6.1.5 [expr.ref]), and
> - if E is a qualified-id, E is not the un-parenthesized operand of the unary & operator (7.6.2.2 [expr.unary.op]),
> the id-expression is transformed into a class member access expression using (*this) as the object expression.

This rule transforms the following valid code (according to the example in 7.5.3 [expr.prim.this] paragraph 5) into invalid code:

      struct A {
       int x;
       int a[sizeof(x)];
       decltype(x) f();
      };

This is a regression introduced by P1787R6 (Declarations and where to find them).

Proposed resolution (reviewed by CWG 2024-06-14) [SUPERSEDED]:

Change in 7.5.5.1 [expr.prim.id.general] paragraph 2 as follows:

> If an id-expression E denotes a non-static non-type member of some class C at a point where the current class (7.5.3 [expr.prim.this]) is X and
> - the point is in a context where 7.5.3 [expr.prim.this] specifies a type for `this`, and
> - E is potentially evaluated or C is X or a base class of X, and
> - E is not the id-expression of a class member access expression (7.6.1.5 [expr.ref]), and
> - if E is a qualified-id, E is not the un-parenthesized operand of the unary & operator (7.6.2.2 [expr.unary.op]),
> the id-expression is transformed into a class member access expression using (*this) as the object expression.

Additional notes (June, 2024)

Unevaluated uses in static member functions are valid (and should not be transformed). However, the resolution above does not address the status quo which does perform the transformation.

Additional notes (July, 2024)

Per #573 and P0847R7 (Deducing this), the intent was that explicit object member functions have naming restrictions similar to other non-static member functions. For example,

      struct C {
        int f(this C);
      };

      using T = decltype(C::f); // error
      int g = C::f(42); // error

The status quo wording does not reflect that desired outcome.

Possible resolution:

1. Change in 7.5.5.1 [expr.prim.id.general] paragraph 2 through 4 as follows:

   > If an id-expression E denotes a non-static non-type member of some class C at a point where the current class (7.5.3 [expr.prim.this]) is X and , the following rules apply. E is said to refer to a local member of some class X if E appears at a point where X is the current class and C is X or a base class of X. E has the following associated cv-qualification relative to some class X:
   > - If E appears in a function-definition, member-declarator, or declarator that declares a member function or member function template of X, at a point after the optional cv-qualifier-seq, the associated cv-qualification is the cv-qualifier-seq, if present, and otherwise empty.
   > - Otherwise, if E appears in a member-declarator declaring a non-static data member of X, at a point within the optional default member initializer, the associated cv-qualification is empty.
   > - Otherwise, E has no associated cv-qualification.

   > E has the following meaning:

   > - If E is the id-expression of a class member access, see 7.6.1.5 [expr.ref].
   > - Otherwise, if E is a qualified-id that is the un-parenthesized operand of the unary & operator, see 7.6.2.2 [expr.unary.op].
   > - Otherwise, if E denotes a non-static data member with declared type `T` and E is not potentially evaluated, the result is an lvalue of type cv `T` where cv is empty unless E refers to a local member of some class X, in which case cv is the associated cv-qualification relative to X (if any). [ Example:

   >         template<int n> class R { };
   >         struct S {
   >           int m;
   >           char g(int&);         // #1
   >           int g(const int&);    // #2
   >           auto f() const -> R<sizeof(g(m))>;     // OK, return type is R<sizeof(int)>
   >         };
   >         int j = sizeof(S::m + 42); // OK

   >   -- end example ]
   > - Otherwise, if E is refers to a local member of some class X:
   >   - If E appears in an implicit object member function, is potentially evaluated, and has a (possibly empty) associated cv-qualification relative to X, E is transformed into a class member access expression using `(*this)` as the object expression. This transformation does not apply in the template definition context (13.8.3.2 [temp.dep.type]).
   >   - Otherwise, E shall not be potentially evaluated; the type and value category of the result is as specified in 7.5.5.2 [expr.prim.id.unqual] and 7.5.5.3 [expr.prim.id.qual].
   > - Otherwise, if E denotes a member enumerator, the result is as specified in 7.5.5.2 [expr.prim.id.unqual] and 7.5.5.3 [expr.prim.id.qual].
   > - Otherwise, the program is ill-formed.
   > - E is potentially evaluated or C is X or a base class of X, and
   > - E is not the id-expression of a class member access expression (7.6.1.5 [expr.ref]), and
   > - if E is a qualified-id, E is not the un-parenthesized operand of the unary & operator (7.6.2.2 [expr.unary.op]),

   > the id-expression is transformed into a class member access expression using `(*this)` as the object expression. [Note 2: If C is not X or a base class of X, the class member access expression is ill-formed. Also, if the id-expression occurs within a static or explicit object member function, the class member access is ill-formed. —end note] This transformation does not apply in the template definition context (13.8.3.2 [temp.dep.type]).

   > If an id-expression E denotes a member M of an anonymous union (11.5.2 [class.union.anon]) U:

   > - If U is a non-static data member, E refers to M as a member of the lookup context of the terminal name of E (after any implicit transformation to a class member access expression). [Example 1: o.x is interpreted as o.u.x, where u names the anonymous union member. —end example]
   > - Otherwise, E is interpreted as a class member access (7.6.1.5 [expr.ref]) that designates the member subobject M of the anonymous union variable for U. [Note 3: Under this interpretation, E no longer denotes a non-static data member. —end note] [Example 2: N::x is interpreted as N::u.x, where u names the anonymous union variable. —end example]

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

   > - as part of a class member access (after any implicit transformation (see above)) in which the object expression refers to the member's class 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. [Example 3:

   >         struct S { int m; };
   >         int i = sizeof(S::m);      // OK
   >         int j = sizeof(S::m + 42); // OK

   >   —end example]

2. Change in 7.6.1.5 [expr.ref] paragraph 5 as follow:

   > 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. `E2` shall denote a member of that class. [Note 3: 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 4: 6.5.5 [basic.lookup.qual] describes how names are looked up after the . and -> operators. —end note]

#### 2738. "denotes a destructor" is missing specification

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

Subclause 7.5.5.2 [expr.prim.id.unqual] 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.5.5 [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.5.2 [expr.prim.id.unqual] paragraph 1 as follows:

> A type-name or decltype-specifier prefixed by `~` denotes the destructor of the named type; see 7.5.5.5 [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.5.5 [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]

#### 2737. Temporary lifetime extension for reference init-captures

Section: 7.5.6.3 [expr.prim.lambda.capture] Status: review Submitter: Tomasz Kamiński Date: 2023-05-23

Consider:

      struct S {};
      const S f();
      auto x = [&ref = f()] { return ref; }

Subclause 7.5.6.3 [expr.prim.lambda.capture] paragraph 6 specifies:

> An init-capture inhabits the lambda scope (6.4.5 [basic.scope.lambda]) of the lambda-expression. An init-capture without ellipsis behaves as if it declares and explicitly captures a variable of the form `“auto init-capture ;”`, except that:
> - if the capture is by copy (see below), the non-static data member declared for the capture and the variable are treated as two different ways of referring to the same object, which has the lifetime of the non-static data member, and no additional copy and destruction is performed, and
> - if the capture is by reference, the variable's lifetime ends when the closure object's lifetime ends.

It is unclear whether the temporary returned by `f()` is lifetime-extended by being bound to a reference init-capture.

There is implementation divergence: gcc rejects the example; clang accepts and extends the lifetime of the temporary; MSVC and EDG accept and do not extend the lifetime.

Proposed resolution (reviewed by CWG 2024-03-01):

Change in 7.5.6.3 [expr.prim.lambda.capture] paragraph 6 as follows:

> An init-capture inhabits the lambda scope (6.4.5 [basic.scope.lambda]) of the lambda-expression. An init-capture without ellipsis behaves as if it declares and explicitly captures a variable of the form `“auto init-capture ;”`, except that:
> - if the capture is by copy (see below), the non-static data member declared for the capture and the variable are treated as two different ways of referring to the same object, which has the lifetime of the non-static data member, and no additional copy and destruction is performed, and
> - if the capture is by reference and the reference would bind to an object that is the result of a temporary expression, the program is ill-formed, and
> - if the capture is by reference, the variable's lifetime ends when the closure object's lifetime ends.

#### 2557. Class member access referring to an unrelated class

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

Consider:

      struct A {
        static int x;
      };

      struct B {
        using type = A;
      };

      int y = B().type::x;

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.5.1 [expr.prim.id.general] 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 [expr.ref] paragraph 5 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:

   >       struct A {
   >         static int x;
   >       };

   >       struct B {
   >         static int x;
   >       };

   >       struct D : B {
   >         using type = A;
   >       };

   >       int y1 = D().B::x;         // OK, B is a base class of D
   >       int y2 = D().type::x;      // error: A is not a base class of D
   >       int y3 = D::type::x;       // OK, evaluates A::x

   > —end example ]

2. Change in 7.6.1.5 [expr.ref] bullet 7.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:

           enum E { e };
           struct X {
             using E::e;
           };
           int f(X x) {
             return x.e;
           }

     —end example ]

3. Change in 7.5.5.1 [expr.prim.id.general] paragraph 4 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.

#### 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 [expr.new] 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:

      #include <iostream>
      struct C {
        void* operator new(std::size_t n) {
          std::cout << "malloc\n";
          return malloc(n);
        }
        void operator delete(void* ptr) {
          std::cout << "free\n";
          free(ptr);
        }
        C() {
          throw 0;
        }
      };
      int main() {
        auto ptr = new C;
      }

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 [except.handle] 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 [expr.new] 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]).

2. Change in 14.3 [except.ctor] 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.

3. Change in 14.3 [except.ctor] 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.8.6.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:

      struct A {};
      struct AA : A { int y; };
      struct B : A { int x; };
      struct C : AA, B {};

      constexpr int f(const A &a) {
        int A::*mp = static_cast<int A::*>(&B::x);
        return a.*mp;
      }

      extern char x[f(static_cast<const AA &>(C{{{}, 13}, {{}, 42}}))];
      extern char x[13];

Subclause 7.6.4 [expr.mptr.oper] 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 [expr.mptr.oper] 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.

#### 2778. Trivial destructor does not imply constant destruction

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

Consider:

      struct Foo {};
      struct Bar {
        mutable Foo f;
      };
      constexpr Bar b{};     // #1

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

Proposed resolution (reviewed by CWG 2024-03-01):

Change in 7.7 [expr.const] paragraph 10 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, and for a hypothetical expression E whose only effect is to destroy a, E would be a core constant expression if any invocation of a trivial destructor for a mutable subobject is ignored and if the lifetime of a and its non-mutable subobjects (but not its mutable subobjects) were considered to start within E.

#### 2800. Instantiating constexpr variables for potential constant evaluation

Section: 7.7 [expr.const] Status: review Submitter: Shafik Yaghmour Date: 2023-09-22

Consider:

      template <typename T>
      struct A {
        T data;

        static const A a;
      };
      template <typename T>
      inline constexpr A<T> A<T>::a {42};

      static_assert(A<int>::a.data == 42);

This ought to be well-formed, but there is no rule that would cause instantiation of `A<int>::a`.

Also consider:

      template<typename T> struct A {
        static T x;
      };
      template<typename T> T A<T>::x = (std::cout << "constructed", T());
      template<typename T> void b(decltype(&A<int>::x));

For the second example, it is undesirable to instantiate `A<int>`.

Possible resolution [SUPERSEDED]:

Change in 7.7 [expr.const] paragraph 3 as follows:

> A variable is potentially-constant if it is constexpr or it has reference or non-volatile const-qualified integral or enumeration type or, for a templated variable, if the definition that would be instantiated if the variable were needed for constant evaluation uses the `constexpr` decl-specifier (13.7.6.2 [temp.spec.partial.match]).

CWG 2023-10-20

The phrasing suggests that `const int` variables instantiated from a variable template are no longer potentially-constant.

Possible resolution:

Change in 7.7 [expr.const] paragraph 3 as follows:

> A variable is potentially-constant if it is constexpr declared with the `constexpr` decl-specifier or it has reference or non-volatile const-qualified integral or enumeration type. Such a variable may be instantiated from a templated variable, in which case the definition that would be instantiated if the variable were needed for constant evaluation is considered (13.7.6.2 [temp.spec.partial.match]).

#### 2976. Transferring control out of a function

Section: 8.10 [stmt.dcl] Status: review Submitter: Artem Koton Date: 2024-12-20

(From submission #656.)

Subclause 8.10 [stmt.dcl] paragraph 2 specifies:

> ... Upon each transfer of control (including sequential execution of statements) within a function from point P to point Q, all block 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.

The phrasing "within a function" can be misread as not applying to transfers out of a function (e.g. execution of a `return` statement).

Possible resolution:

Change in 8.10 [stmt.dcl] paragraph 2 as follows:

> A block variable with automatic storage duration (6.8.6.4 [basic.stc.auto]) is active everywhere in the scope to which it belongs after its init-declarator . Upon each transfer of control (including sequential execution of statements) within or out of a function from point P to point Q, all block variables with automatic storage duration that are active at P and not at Q are destroyed in the reverse order of their construction. Then, all block variables with automatic storage duration that are active at Q but not at P are initialized in declaration order; unless all such variables have vacuous initialization (6.8.4 [basic.life]), the transfer of control shall not be a jump. [ Footnote: ...] When a declaration-statement is executed, P and Q are the points immediately before and after it; when a function returns, Q is after its body.

#### 1670. `auto` as conversion-type-id

Section: 9.2.9.7 [dcl.spec.auto] Status: review Submitter: Richard Smith Date: 2013-04-26 Liaison: EWG
N3690 comment FI 4

(See also submission #736.)

The current wording allows something like

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

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 2493 for additional details.

Additional note (August, 2025):

CWG appears to be leaning towards making declarations of `operator auto` ill-formed. Forwarding to EWG for approval of that direction via paper issue #2409, by decision of the CWG chair.

Possible resolution:

Change in 9.2.9.7.1 [dcl.spec.auto.general] paragraph 4 as follows:

> A placeholder type can appear in the decl-specifier-seq or type-specifier-seq in the declared return type of a function declarator that declares a function other than a conversion function (11.4.8.3 [class.conv.fct]); 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]).

#### 2412. SFINAE vs undeduced placeholder type

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

The status of the following example is not clear:

      template <typename T> auto foo(T);  // Not defined

      template <typename T> struct FooCallable {
        template<class U>
        static constexpr bool check_foo_callable(...) { return false; }

        template<class U, class = decltype(foo(U{})) >
        static constexpr bool check_foo_callable(int) { return true; }

        static constexpr bool value = check_foo_callable<T>(0);
      };
      static_assert(FooCallable<int>::value == false, "");

The `static_assert` causes the evaluation of the default template argument `decltype(foo<int>(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.7 [dcl.spec.auto] 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 [temp.inst] 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<int>`. 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.7.1 [dcl.spec.auto.general] 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 <typename T> auto f(T);     // not defined

>       template <typename T> struct F {
>         template<class U>
>         static constexpr bool g(...) { return false; }

>         template<class U, class = decltype(f(U{})) >
>         static constexpr bool g(int) { return true; }

>         static constexpr bool value = g<T>(0);
>       };
>       static_assert(F<int>::value == false, "");

> -- end example ]

#### 2228. Ambiguity resolution for cast to function type

Section: 9.3.3 [dcl.ambig.res] Status: review Submitter: Richard Smith Date: 2016-02-02 Liaison: EWG

Consider:

      int x = (int()) + 5;

This is ill-formed, because 9.3.3 [dcl.ambig.res] paragraph 2 specifies:

> An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.

and thus `int()` is interpreted as a type-id instead of as a function-style cast, so this is an ill-formed cast to a function type.

This seems to be the wrong disambiguation for all cases where there is a choice between a C-style cast and a parenthesized expression: in all those cases, the C-style cast interpretation results in a cast to a function type, which is always ill-formed.

Further, there is implementation divergence in the handling of this example:

      struct T { int operator++(int); T operator[](int); };
      int a = (T()[3])++; // not a cast

EWG 2022-11-11

This is tracked in github issue cplusplus/papers#1376.

Additional notes (May, 2025)

Also consider this example:

      (S())[]->A<int>;              // OK, constructor call
      (S())[]->A<int> {return {};}; // error: C-style cast of lambda

Without the suggested simple rule that `(T())` is never a conversion to a function type (which is always semantically ill-formed), syntactic disambiguation would require analysis whether the thing after the arrow is a type (i.e. a trailing return type) or an expression (for `operator->`).

Suggested resolution (2019-01-02):

1. Change in 7.6.2 [expr.unary] paragraph 1 as follows:

   >     unary-expression :
   >       postfix-expression
   >       unary-operator cast-expression
   >       ++ cast-expression
   >       -- cast-expression
   >       await-expression
   >       sizeof unary-expression
   >       sizeof ( nofun-type-id )
   >       sizeof ... ( identifier )
   >       alignof ( nofun-type-id )
   >       noexcept-expression
   >       new-expression
   >       delete-expression

2. Change in 7.6.2.5 [expr.sizeof] paragraph 1 as follows:

   > The operand is either an expression, which is an unevaluated operand (7.2.3 [expr.context]), or a parenthesized nofun-type-id.

3. Change in 7.6.2.6 [expr.alignof] paragraph 1 as follows:

   > The operand shall be a nofun-type-id representing a complete object type, or an array thereof, or a reference to one of those types.

4. Replace type-id with nofun-type-id throughout 7.6.2.8 [expr.new], for example in paragraph 1:

   >       new-expression :
   >         ::opt new new-placementopt new-type-id new-initializeropt
   >         ::opt new new-placementopt ( nofun-type-id ) new-initializeropt

5. Change in 7.6.3 [expr.cast] paragraph 2 as follows:

   >       cast-expression :
   >         unary-expression
   >         ( nofun-type-id ) cast-expression

6. Change in 9.2.9.7.1 [dcl.spec.auto.general] paragraph 6 as follows:

   > A placeholder type can also be used in the type-specifier-seq of the new-type-id or in the nofun-type-id of a new-expression (7.6.2.8 [expr.new]). In such a nofun-type-id, the placeholder type shall appear as one of the type-specifiers in the type-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a type-specifier .

7. Change in 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 2 as follows:

   > A placeholder for a deduced class type can also be used in the type-specifier-seq in the new-type-id or nofun-type-id of a new-expression (7.6.2.8 [expr.new]), as the simple-type-specifier in an explicit type conversion (functional notation) (7.6.1.4 [expr.type.conv]), or as the type-specifier in the parameter-declaration of a template-parameter (13.2 [temp.param]). A placeholder for a deduced class type shall not appear in any other context.

8. Change in 9.3.1 [dcl.decl.general] paragraph 6 as follows, to fix `auto (*p)() -> int(X());` (now a function pointer initialized by `X()`):

   >       trailing-return-type :
   >         -> nofun-type-id

9. Change in 9.3.2 [dcl.name] paragraph 1 as follows:

   >       nofun-type-id:
   >         type-specifier-seq nofun-declaratoropt

   >       nofun-declarator:
   >         ptr-nofun-declarator
   >         noptr-nofun-declarator parameters-and-qualifiers trailing-return-type

   >       ptr-nofun-declarator:
   >         noptr-nofun-declarator
   >         ptr-operator ptr-nofun-declaratoropt

   >       noptr-nofun-declarator:
   >         noptr-nofun-declarator parameters-and-qualifiers
   >         noptr-nofun-declaratoropt [ constant-expressionopt ] attribute-specifier-seqopt
   >         ( ptr-nofun-declarator )

   > It is possible to identify uniquely the location in the abstract-declarator or nofun-declarator where the identifier would appear if the construction were a declarator in a declaration. The named type is then the same as the type of the hypothetical identifier.

10. Change in 9.3.3 [dcl.ambig.res] paragraph 2 as follows:

    > An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id. [ Note: No such ambiguity can arise between an expression and a nofun-type-id. -- end note ] However, a construct that can syntactically be a type-id whose outermost abstract-declarator would match the grammar of an abstract-declarator with a trailing-return-type is considered a type-id only if it starts with auto. [Example 2 :

    >       template <class T> struct X {};
    >       template <int N> struct Y {};
    >       X<int()> a;   // type-id
    >       X<int(1)> b;  // expression (ill-formed)
    >       Y<int()> c;   // type-id (ill-formed)
    >       Y<int(1)> d;  // expression
    >       void foo(signed char a) {
    >         sizeof(int());    // type-id (ill-formed) expression
    >         sizeof(int(a));   // expression
    >         sizeof(int(unsigned(a)));    // type-id (ill-formed) expression
    >         (int())+1;              // type-id (ill-formed) expression
    >         (int(a))+1;             // expression
    >         (int(unsigned(a)))+1;   // type-id (ill-formed) expression
    >       }
    >       typedef struct BB { int C[2]; } *B, C;
    >       void g() {
    >       sizeof(B()->C[1]);     // OK, sizeof(expression)
    >       sizeof(auto()->C[1]);  // error: sizeof of a function returning an array
    >     }

    > -- end example ]

11. Change in 9.13.1 [dcl.attr.grammar] paragraph 1 as follows:

    >     alignment-specifier :
    >       alignas ( nofun-type-id ...opt )
    >       alignas ( constant-expression ...opt )

12. Change in 9.13.2 [dcl.align] paragraph 3 as follows:

    > An alignment-specifier of the form `alignas( nofun-type-id )` has the same effect as `alignas(alignof( nofun-type-id ))` (7.6.2.6 [expr.alignof]).

13. Do not change 13.8.1 [temp.res.general] paragraph 4.

14. Change in 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

    > Expressions of the following forms are type-dependent only if the type specified by the type-id, nofun-type-id, simple-type-specifier, typename-specifier, or new-type-id is dependent, even if any subexpression is type-dependent:

    >       simple-type-specifier ( expression-listopt )
    >       simple-type-specifier braced-init-list
    >       typename-specifier ( expression-listopt )
    >       typename-specifier braced-init-list
    >       ::opt new new-placementopt new-type-id new-initializeropt
    >       ::opt new new-placementopt ( nofun-type-id ) new-initializeropt
    >       dynamic_cast < type-id > ( expression )
    >       static_cast < type-id > ( expression )
    >       const_cast < type-id > ( expression )
    >       reinterpret_cast < type-id > ( expression )
    >       ( nofun-type-id ) cast-expression

15. Change in 13.8.3.3 [temp.dep.expr] paragraph 4 as follows:

    >       literal
    >       sizeof unary-expression
    >       sizeof ( nofun-type-id )
    >       sizeof ... ( identifier )
    >       alignof ( nofun-type-id )
    >       typeid ( expression )
    >       typeid ( type-id )
    >       ::opt delete cast-expression
    >       ::opt delete [ ] cast-expression
    >       throw assignment-expressionopt
    >       noexcept ( expression )
    >       requires-expression

16. Change in 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:

    > Expressions of the following form are value-dependent if the unary-expression or expression is type-dependent or the type-id or nofun-type-id is dependent:

    >       sizeof unary-expression
    >       sizeof ( nofun-type-id )
    >       typeid ( expression )
    >       typeid ( type-id )
    >       alignof ( nofun-type-id )

17. Change in 13.8.3.4 [temp.dep.constexpr] paragraph 3 as follows:

    > Expressions of the following form are value-dependent if either the type-id, nofun-type-id, simple-type-specifier, or typename-specifier is dependent or the expression or cast-expression is value-dependent or any expression in the expression-list is value-dependent or any assignment-expression in the braced-init-list is value-dependent:

    >       simple-type-specifier ( expression-listopt )
    >       typename-specifier ( expression-listopt )
    >       simple-type-specifier braced-init-list
    >       typename-specifier braced-init-list
    >       static_cast < type-id > ( expression )
    >       const_cast < type-id > ( expression )
    >       reinterpret_cast < type-id > ( expression )
    >       dynamic_cast < type-id > ( expression )
    >       ( nofun-type-id ) cast-expression

EWG 2025-06-16

EWG agrees with the direction represented by the wording for the May, 2025 notes, above.

#### 1001. Parameter type adjustment in dependent parameter types

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

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

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

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

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

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

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

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

Rationale (November, 2010):

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

Notes, January, 2012:

Additional discussion of this issue arose regarding the following example:

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

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

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

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

(See also issue 1322.)

Additional notes (February, 2024)

There is implementation divergence for the following example (clang and EDG accept, gcc and MSVC reject):

      template<typename U>
      int f(const U t);

      int v;
      auto test = f(v);

      template<typename U>
      int f(U t) {
       static_assert(std::is_const<decltype(t)>::value, "t should be non-const");
       return 0;
      }

Possible resolution (option 1):

Change in 9.3.4.6 [dcl.fct] paragraph 4 as follows:

> 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 parameter-declaration (9.3 [dcl.decl]). After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a non-dependent 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. ...

Additional notes (December, 2024)

The resolution suggested above causes errors in ostensibly more common situations such as this one, requiring an Annex C entry:

      template <class T>
      void f(T*);                 // #1

      template <class T>
      void f(T* const) {}         // redeclaration of #2?

      int main() {
        f((int*)0);               // was ok, becomes ambiguous
      }

Possible resolution (option 2):

1. Change in 9.3.4.6 [dcl.fct] paragraph 4 as follows:

   > 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 parameter-declaration (9.3 [dcl.decl]). After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. If a function parameter type is adjusted from "array of `T`" and acquires the array type through a dependent type, and any top-level cv-qualifier modifies the dependent parameter type in any declaration of the templated function, the program is ill-formed; no diagnostic is required unless a cv-qualifier modifies the parameter type in a reachable definition of the function. 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. ...

   > [ Example:

   > ...

   >       void h(int x(const int));   // #3
   >       void h(int (*)(int)) {}     // defines #3

   >       template<class T>
   >       int i(T);                   // #4
   >       template<class T>
   >       int i(const T) {}           // defines #4
   >       int v = i<int[]>(0);        // error: cv-qualified array parameter type in definition of template

   > -- end example]

2. Change in 13.2 [temp.param] paragraph 6 as follows:

   > A non-type template-parameter shall have one of the following (possibly cv-qualified) types:
   > - a structural type (see below),
   > - a type that contains a placeholder type (9.2.9.7 [dcl.spec.auto]), or
   > - a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]).
   > The top-level cv-qualifiers on the template-parameter are ignored when determining its type.

3. Change in 13.2 [temp.param] paragraph 10 as follows:

   > A non-type template-parameter of type “array of T” or of function type T is adjusted to be of type “pointer to T”. If the type of a non-type template-parameter is adjusted from "array of T" and acquires the array type through a dependent type, and any top-level cv-qualifier modifies the dependent type in any declaration of the template, the program is ill-formed; no diagnostic is required unless a cv-qualifier modifies the type of the template-parameter in a reachable definition of the template. [ Example:

   >       template<int* a>
   >       struct R { /* ... */ };
   >       template<int b[5]> struct S { /* ... */ };
   >       int p;
   >       R<&p> w;      // OK
   >       S<&p> x;      // OK due to parameter adjustment
   >       int v[5];
   >       R<v> y;       // OK due to implicit argument conversion
   >       S<v> z;       // OK due to both adjustment and conversion

   >       template<class T, const T x>
   >       struct U { /* ... */ };
   >       U<int[], nullptr> u;   // error: const array adjustment

   > -- end example ]

   > The top-level cv-qualifiers on the (possibly adjusted) type of a template parameter are ignored.

Additional notes (January, 2025)

Issue 1321 resolved to requiring substitution into the first (or, nowadays, a first) declaration (13.8.1 [temp.res.general] paragraph 1), which conflicts with special-casing the defining declaration here.

#### 2553. Restrictions on explicit object member functions

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

Subclause 9.3.4.6 [dcl.fct] 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 [dcl.fct] 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 [dcl.fct] 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 [class.virtual] 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 [dcl.fct] 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 [dcl.fct] 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 [class.free] 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 [class.free] 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 [class.free] 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 [class.virtual] 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, ...

CWG 2023-11-09

Progress of this issue is blocked on issue 2554.

#### 2977. Initialization with string literals

Section: 9.5.1 [dcl.init.general] Status: review Submitter: Jiang An Date: 2024-12-27

(From submission #661.)

Subclause 9.5.1 [dcl.init.general] bullet 16.3 specifies:

> - ...
> - If the destination type is an array of characters, an array of char8_t, an array of char16_t, an array of char32_t, or an array of wchar_t, and the initializer is a string-literal, see 9.5.3 [dcl.init.string].
> - ...

However, the initializer will be of the form `= string-literal` or `(string-literal)`, neither of which is a string-literal, obviously.

On a slightly related note, implementations other than gcc in strict modes support parenthesized string literals, e.g.

      const char s1[] = ("foo");
      const char s2[] = {("foo")};

The latter is clearly not covered by 9.5.5 [dcl.init.list] bullet 3.3:

> - ...
> - Otherwise, if T is a character array and the initializer list has a single element that is an appropriately-typed string-literal (9.5.3 [dcl.init.string]), initialization is performed as described in that subclause.
> - ...

Possible resolution [SUPERSEDED]:

Change in 9.5.1 [dcl.init.general] bullet 16.3 as follows:

> - ...
> - If the destination type is an array of characters, an array of char8_t, an array of char16_t, an array of char32_t, or an array of wchar_t, and the initializer initializer is a string-literal of the form `= string-literal` or of the form `( string-literal )`, see 9.5.3 [dcl.init.string].
> - ...

Additional notes (January 2025)

The resolution above does not allow this example:

      S s = {"abc"};

This recurses back from 9.5.2 [dcl.init.aggr] bullet 4.3 to 9.5.1 [dcl.init.general].

Possible resolution:

Change in 9.5.1 [dcl.init.general] bullet 16.3 as follows:

> - ...
> - If the destination type is an array of characters, an array of char8_t, an array of char16_t, an array of char32_t, or an array of wchar_t, and the initializer initialization is from a an unparenthesized string-literal, see 9.5.3 [dcl.init.string].
> - ...

#### 2563. Initialization of coroutine result object

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

Subclause 9.6.4 [dcl.fct.def.coroutine] 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 issue 2562),
- 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.6.4 [dcl.fct.def.coroutine] 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.

Proposed resolution (May, 2025):

1. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 5 as follows:

   > A coroutine behaves as if the top-level cv-qualifiers in all parameter-declarations in the declarator of its function-definition were removed and its function-body were replaced by the following replacement body:

   >       {
   >         promise-type promise promise-constructor-arguments ;
   >         get-return-object-invocation ;
   >         try {
   >           co_await promise .initial_suspend() ;
   >           function-body
   >         } catch ( ... ) {
   >           if (!initial-await-resume-called )
   >             throw ;
   >           promise .unhandled_exception() ;
   >         }
   >       final-suspend :
   >         co_await promise .final_suspend() ;
   >       }

   > where
   > - ...
   > - promise-constructor-arguments is determined as follows: ...
   > - get-return-object-invocation is as follows:
   >   - if the return type `R` of `promise.get_return_object()` is cv `void`, then get-return-object-invocation is `promise.get_return_object()`, and the return type of the coroutine shall also be cv `void`;
   >   - otherwise, if `R` is the same class type as the return type of the coroutine (ignoring cv-qualification), and the implementation does not create a temporary to hold the result object (6.8.7 [class.temporary]), then get-return-object-invocation initializes the prvalue result object of the call to the coroutine from `promise.get_return_object()`;
   >   - otherwise, get-return-object-invocation initializes a variable with the exposition-only name `gro` as if by

   >           decltype(auto) gro = promise.get_return_object();

   > - a coroutine is suspended at the initial suspend point if ...

2. Replace 9.6.4 [dcl.fct.def.coroutine] paragraph 8 as follows:

   > 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-suspeend and is invoked at most once.

   > If get-return-object-invocation defines a variable `gro` and the coroutine returns to its caller, whether by suspending (7.6.2.4 [expr.await]) or completing without ever suspending (8.8.5 [stmt.return.coroutine]), it is as if by `return gro`. This return exits the scope of `gro`. It exits the scope of `promise` only if the coroutine completed without suspending. [Note: The operand of the return might be move-eligible (7.5.5.2 [expr.prim.id.unqual]). --end note]

3. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 11 as follows:

   > ... If the allocation function returns nullptr, the coroutine transfers control to the caller of the coroutine and the return value is obtained by a call to as if by `return` `T::get_return_object_on_allocation_failure();`, where `T` is the promise type. ...

#### 2932. Value range of empty enumeration

Section: 9.8.1 [dcl.enum] Status: review Submitter: Benjamin Sch. Date: 2024-08-31 Liaison: EWG

(From submission #604.)

Consider:

      enum E { };
      constexpr auto x = static_cast<E>(-1);

It is unclear whether the hypothetical integer type for `E` is a signed integer type of width 1 or an unsigned integer type of width 0, which does not have a signed counterpart and thus does not exist. The former choice makes the example well-formed, the latter one ill-formed. Before P1236, the specification was clear.

Proposed resolution:

Change in 9.8.1 [dcl.enum] paragraph 8 as follows:

> For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, the values of the enumeration are the values representable by a hypothetical integer type with minimal width M such that all enumerators can be represented; if if the enumerator-list is empty or all enumerators have value 0, the values of the enumeration consist of the single value 0 and M is 0. The width of the smallest bit-field large enough to hold all the values of the enumeration type is the maximum of 1 and M. [ Note: It is possible to define an enumeration that has values not defined by any of its enumerators. -- end note ] If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0. [Footnote: ... ]

CWG 2024-10-25

A value needs at least one bit to be represented, which always allows to differentiate two values. Thus, having the "values of an enumeration" be a single value makes no sense.

The wording in C++17 [dcl.enum] p8 said:

> For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a ones' complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| - K, |emax|) and equal to 2M - 1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax + K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M, 1) if bmin is zero and M + 1 otherwise. It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.

If there is a single enumerator with value 0, emin = 0 and emax = 0. A two's complement representation is now required, thus K=1. bmax is 0 with M = 0. bmin is 0.

CWG 2025-11-08

The wording in the proposed resolution restores the C++17 situation. However, both clang and gcc agree that the values of an empty enumeration or one with all enumerators having value 0 are actually 0 and 1, reflecting the single bit of storage needed for the representation. CWG observed that empty classes also occupy one byte of storage, even though they hold no values at all.

EWG should decide whether to keep the C++17 rule or adjust the wording to reflect the implementation practice. Forwarded to EWG via paper issue 2138.

#### 2885. Non-eligible trivial default constructors

Section: 11.4.5.2 [class.default.ctor] Status: review Submitter: Roy Jacobson Date: 2022-11-26 Liaison: LWG

(From submission #521.)

Consider:

      template <class T>
      struct A {
        A() requires (false) = default;
        A() : t(42) {}
        T t;
      };

      struct B : A<int> {};

According to the current wording, class `B` has a trivial default constructor, which is undesirable.

Proposed resolution (approved by CWG 2024-05-31):

Change in 11.4.5.2 [class.default.ctor] paragraph 3 as follows:

> A default constructor for class `X` is trivial if it is not user-provided and if:
> - its class `X` has no virtual functions (11.7.3 [class.virtual]) and no virtual base classes (11.7.2 [class.mi]), and
> - no non-static data member of its class `X` has a default member initializer (11.4 [class.mem]), and
> - all the direct base classes of its class have trivial default constructors, and
> - the constructor selected to default-initialize each direct base class subobject of `X` is trivial,
> - for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
> - for each direct non-static data member of `X` that is of class type (or array thereof), the constructor selected to default-initialize that member (or an array element of that member) is trivial.
> Otherwise, the default constructor is non-trivial.

#### 3022. Redundant specification of explicit destructor calls

Section: 11.4.7 [class.dtor] Status: review Submitter: Timur Doumler Date: 2025-04-13

Subclause 11.4.7 [class.dtor] paragraph 16 explains explicit destructor calls, but that is redundant with 7.5.5.2 [expr.prim.id.unqual], 7.5.5.5 [expr.prim.id.dtor], and the usual rules for function calls (7.6.1.3 [expr.call]).

Possible resolution:

1. Change in 7.5.5.2 [expr.prim.id.unqual] paragraph 1 as follows:

   > An identifier is only an id-expression if it has been suitably declared (Clause Clause 9 [dcl]) or if it appears as part of a declarator-id (9.3 [dcl.decl]). [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]. —end note] A type-name or computed-type-specifier prefixed by `~` denotes the destructor of the type so named; see 7.5.5.5 [expr.prim.id.dtor]. —end note]

2. Change in 11.4.7 [class.dtor] paragraph 16 as follows:

   > [ Note: In an explicit destructor call, the destructor is specified by a ~ followed by a type-name or computed-type-specifier that denotes the destructor's class type (7.5.5.2 [expr.prim.id.unqual]). The invocation of a destructor is subject to the usual rules for member functions (11.4.2 [class.mfct]); that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior. [Note 7: Invoking delete on a null pointer does not call the destructor; see 7.6.2.9 [expr.delete]. —end note] [Example: ... -- end example] —end note]

3. Change in 13.4.1 [temp.arg.general] paragraph 6 as follows:

   > [ Note: An explicit destructor call (11.4.7 [class.dtor]) for an object that has a type that is a class template specialization may can explicitly specify the template-arguments. [ Example: ... -- end example ] -- end note]

#### 1721. Diagnosing ODR violations for static data members

Section: 11.4.9.3 [class.static.data] Status: review Submitter: Mike Miller Date: 2013-07-31

Describing the handling of static data members with brace-or-equal-initializers, 11.4.9.3 [class.static.data] paragraph 4 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.”

Additional notes (March, 2024)

This paragraph has more issues and is largely redundant with 6.2 [basic.def] paragraph 2.3 and paragraph 2.4.

Possible resolution [SUPERSEDED]:

Change 11.4.9.3 [class.static.data] paragraph 4 as follows:

> If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (7.7 [expr.const]). 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 declaration of an inline static data member (which is a definition) 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 D.7 [depr.static.constexpr]). Declarations of other static data members shall not specify a brace-or-equal-initializer.

> If a declaration of a non-inline static data member in a class definition specifies a brace-or-equal-initializer, the member shall be of non-volatile const integral or enumeration type and shall be constant-initialized (7.7 [expr.const]), and the definition (if any) shall not specify a brace-or-equal-initializer.

Additional notes (December, 2024)

9.5.1 [dcl.init.general] paragraph 2 and paragraph 3 serve no normative purpose and should be struck.

Possible resolution:

1. Remove 9.5.1 [dcl.init.general] paragraph 2 and paragraph 3:

   > Except for objects declared with the constexpr specifier, for which see 9.2.6 [dcl.constexpr], an initializer in the definition of a variable can consist of arbitrary expressions involving literals and previously declared variables and functions, regardless of the variable's storage duration. [Example 1: ... —end example]

   > [Note 2: Default arguments are more restricted; see 9.3.4.7 [dcl.fct.default]. —end note]

2. Change 11.4.9.3 [class.static.data] paragraph 4 as follows:

   > If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (7.7 [expr.const]). 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 declaration of an inline static data member (which is a definition) 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 D.7 [depr.static.constexpr]). Declarations of other static data members shall not specify a brace-or-equal-initializer.

   > If a declaration of a non-inline static data member in a class definition specifies a brace-or-equal-initializer, the member shall be of non-volatile const integral or enumeration type and shall be constant-initialized (7.7 [expr.const]), and the definition (if any) shall not specify a brace-or-equal-initializer.

#### 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:

      struct B {
        virtual void f();   // #1
      };

      struct D : B {
        void f();           // #2
      };

Subclause 11.7.3 [class.virtual] 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 [basic.scope.scope] 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 [basic.scope.scope] 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 issue 2553.

Proposed resolution:

1. Change in 11.7.3 [class.virtual] 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 [class.virtual] paragraph 7 as follows:

   > The ref-qualifier , or lack thereof, of an overriding function shall be the same as that of the overridden function.

#### 1883. Protected access to constructors in mem-initializers

Section: 11.8.5 [class.protected] Status: review Submitter: Daveed Vandevoorde Date: 2014-02-26

According to 11.8.5 [class.protected] 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.

Proposed resolution (March, 2025):

Change in 11.8.5 [class.protected] paragraph 1 as follows and move the example to a separate numbered paragraph:

> An additional access check beyond those described earlier in 11.8 [class.access] is applied when a non-static data member or non-static member function is a protected member of its naming class (11.8.3 [class.access.base]). [ Footnote: ... ] As described earlier, access to a protected member is granted because the reference occurs in a friend or direct member of some class C. If the access is to form a pointer to member (7.6.2.2 [expr.unary.op]), the nested-name-specifier shall denote C or a class derived from C. Otherwise, if the member is a constructor, either C shall be the naming class or the access shall be the constructor call for a base class subobject of C performed by a constructor of C as described in 11.9.3 [class.base.init]. All other accesses involve a (possibly implicit) object expression (7.6.1.5 [expr.ref]). In this case Unless the access is an implicit destructor call for a base class subobject by a destructor of C (11.4.7 [class.dtor]), the class of the object expression shall be C or a class derived from C.

> [Example 1 :

>       class B {
>       protected:
>         int i;
>         static int j;
>         B(int = 0);
>         ~B();
>       };
>       class D1 : public B {
>         D1() : B(1) {                 // OK
>           B b(*this);                 // error: destructor of B is protected
>         }
>         ~D1() = default;              // OK
>       };

> ... -- end example ]

#### 2756. Completion of initialization by delegating constructor

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

Subclause 6.8.4 [basic.life] 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.5 [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 [except.ctor] 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) [SUPERSEDED]:

1. Split and change 11.9.3 [class.base.init] 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.8.4 [basic.life]) before all delegating constructors have completed. -- end note]

2. Change in 6.8.4 [basic.life] 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.5 [dcl.init], 11.9.3 [class.base.init]),
   > except that ...

CWG 2023-10-20

Utterances about "during construction or destruction" in 11.9.5 [class.cdtor] need to be adjusted.

Possible resolution:

1. Split and change 11.9.3 [class.base.init] 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.8.4 [basic.life]) before all delegating constructors have completed. -- end note]

2. Change in 6.8.4 [basic.life] 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.5 [dcl.init], 11.9.3 [class.base.init]),
   > except that ...

3. Change in 11.9.5 [class.cdtor] paragraph 2 as follows:

   > During the construction initialization of an 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.

4. Change in 11.9.5 [class.cdtor] paragraph 4 as follows:

   > 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 initialization 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 being initialized or destroyed, 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 x or one of its base class subobjects, the behavior is undefined.

5. Change in 11.9.5 [class.cdtor] paragraph 5 as follows:

   > 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 default member initializer (11.4 [class.mem]) 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 being initialized or destroyed, 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 being initialized or destroyed and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.

6. Change in 11.9.5 [class.cdtor] paragraph 6 as follows:

   > 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 default member 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 being initialized or destroyed, 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 being initialized or destroyed 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.

#### 2757. Deleting or deallocating storage of an object during its construction

Section: 11.9.5 [class.cdtor] Status: review Submitter: Jiang An Date: 2023-06-14

Subclause 6.8.4 [basic.life] paragraph 6 specifies:

> ... For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, ...

However, the referenced subclause does not discuss deleting the object or deallocating its storage.

See also issue 2258.

Proposed resolution (reviewed by CWG 2023-10-20):

1. Insert before 11.9.5 [class.cdtor] paragraph 2 as follows:

   > During the initialization or destruction of an object, invoking the destructor for the object or releasing or reusing (6.8.4 [basic.life]) the storage which the object occupies results in undefined behavior. [ Note: Creating an object nested within some object o does not reuse the storage for o. -- end note ] [ Example:

   >       struct A() {
   >         A() {
   >           ::operator delete(this);
   >         }
   >       };

   >       A *p1 = new A;  // undefined behavior

   >       struct B() {
   >         B() {
   >           delete this;
   >         }
   >       };

   >       B *p2 = new B;  // undefined behavior

   >       struct S {
   >         constexpr S() { this->~S(); }
   >         constexpr S(int) {}
   >       };

   >       constexpr int f() {
   >         S s(0);
   >         s.~S();
   >         std::construct_at(&s);  // #1
   >         return 0;
   >       }

   >       constexpr int x = f();  // error: undefined behavior at #1

   > -- end example ]

   > During the construction of an object, if the value of the object ...

2. Change in 11.9.5 [class.cdtor] paragraph 4 as follows:

   > Member functions, including virtual functions (11.7.3 [class.virtual]), other than the destructor can be called during construction or destruction (11.9.3 [class.base.init]). When a virtual function (11.7.3 [class.virtual]) 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 ...

#### 2797. Meaning of "corresponds" for rewritten operator candidates

Section: 12.2.2.3 [over.match.oper] Status: review Submitter: Corentin Jabot Date: 2023-09-17 Liaison: EWG

Subclause 12.2.2.3 [over.match.oper] paragraph 4 specifies:

> A non-template function or function template F named operator== is a rewrite target with first operand o unless a search for the name operator!= in the scope S from the instantiation context of the operator expression finds a function or function template that would correspond (6.4.1 [basic.scope.scope]) to F if its name were operator==, where S is the scope of the class type of o if F is a class member, and the namespace scope of which F is a member otherwise. A function template specialization named operator== is a rewrite target if its function template is a rewrite target.

The meaning of "corresponds" has changed with paper P0847 such that this example from the Working Paper is now ill-formed:

      struct B {
        bool operator==(const B&);
      };
      struct C : B {
        C();
        C(B);
        bool operator!=(const B&);
      };
      bool c1 = B() == C();    // was OK, now ill-formed

The reason is that the definition of "corresponds" now also considers the object parameters, which are different in the example above.

Possible resolution [SUPERSEDED]:

1. Change and split in 6.4.1 [basic.scope.scope] paragraph 3 as follows:

   > ...

   > Two functions have corresponding non-object signatures if they have the same non-object-parameter-type-list. Two function templates have corresponding non-object signatures if their template-parameter-lists have the same length, their corresponding template-parameter s are equivalent, and they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters. Two functions or function templates have corresponding signatures if they have corresponding non-object signatures and, if both are non-static members, they have corresponding object parameters.

2. Change in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

   > Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless
   > - either is a using-declarator , or
   > - one declares a type (not a typedef-name) and the other declares a variable, non-static data member other than of an anonymous union (11.5.2 [class.union.anon]), enumerator, function, or function template, or
   > - each declares a function or function template and they do not declare corresponding overloads.
   > Two function or function template declarations declare corresponding overloads if:
   > - 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, corresponding signatures or both declare function templates with corresponding signatures and equivalent template-heads and trailing requires-clauses (if any).
   > - both have equivalent (13.7.7.2 [temp.over.link]) template-heads and trailing requires-clauses (if any, except as specified in 13.7.5 [temp.friend]).

3. Change in 12.2.2.3 [over.match.oper] paragraph 4 as follows:

   > A non-template function or function template F named operator== is a rewrite target with first operand o unless a search for the name operator!= in the scope S from the instantiation context of the operator expression finds a function or function template `F2` such that would correspond `F` and `F2` have corresponding non-object signatures (6.4.1 [basic.scope.scope]) to F if its name were operator==, where S is the scope of the class type of o if F is a class member, and the namespace scope of which F is a member otherwise. A function template specialization named operator== is a rewrite target if its function template is a rewrite target.

CWG 2023-10-06

CWG raised the question to EWG whether the special rule that allows the example in this issue for backward compatibility should be extended to member functions with explicit object parameters. This is tracked via paper issue 1645.

EWG 2024-03-18

EWG has no consensus to extend the rules to explicit object parameters.

Possible resolutionv [SUPERSEDED]:

1. Change and split in 6.4.1 [basic.scope.scope] paragraph 3 as follows:

   > ...

   > Two functions have corresponding non-object signatures if they have the same non-object-parameter-type-list. Two function templates have corresponding non-object signatures if their template-parameter-lists have the same length, their corresponding template-parameter s are equivalent, and they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters. Two functions or function templates have corresponding signatures if they have corresponding non-object signatures and, if both are non-static members, they have corresponding object parameters.

2. Change in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

   > Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless
   > - either is a using-declarator , or
   > - one declares a type (not a typedef-name) and the other declares a variable, non-static data member other than of an anonymous union (11.5.2 [class.union.anon]), enumerator, function, or function template, or
   > - each declares a function or function template and they do not declare corresponding overloads.
   > Two function or function template declarations declare corresponding overloads if:
   > - 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, corresponding signatures or both declare function templates with corresponding signatures and equivalent template-heads and trailing requires-clauses (if any).
   > - both have equivalent (13.7.7.2 [temp.over.link]) template-heads and trailing requires-clauses (if any, except as specified in 13.7.5 [temp.friend]).

3. Change in 12.2.2.3 [over.match.oper] paragraph 4 as follows:

   > A non-template function or function template F named operator== is a rewrite target with first operand o unless a search for the name operator!= in the scope S from the instantiation context of the operator expression finds a function or function template `F2` such that would correspond `F2` is an implicit object member function and `F` and `F2` have corresponding non-object signatures (6.4.1 [basic.scope.scope]) to F if its name were operator==, where S is the scope of the class type of o if F is a class member, and the namespace scope of which F is a member otherwise. A function template specialization named operator== is a rewrite target if its function template is a rewrite target.

CWG 2024-11-18

Subclause 9.10 [namespace.udecl] paragraph 11 needs to change. Limit to both F and F2 being implicit object member functions.

Possible resolution [SUPERSEDED]:

1. Change and split in 6.4.1 [basic.scope.scope] paragraph 3 as follows:

   > ...

   > Two functions have corresponding non-object signatures if they have the same non-object-parameter-type-list. Two function templates have corresponding non-object signatures if their template-parameter-lists have the same length, their corresponding template-parameter s are equivalent, and they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters. Two functions or function templates have corresponding signatures if they have corresponding non-object signatures and, if both are non-static members, they have corresponding object parameters.

2. Change in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

   > Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless
   > - either is a using-declarator , or
   > - one declares a type (not a typedef-name) and the other declares a variable, non-static data member other than of an anonymous union (11.5.2 [class.union.anon]), enumerator, function, or function template, or
   > - each declares a function or function template and they do not declare corresponding overloads.
   > Two function or function template declarations declare corresponding overloads if:
   > - 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, corresponding signatures or both declare function templates with corresponding signatures and equivalent template-heads and trailing requires-clauses (if any).
   > - both have equivalent (13.7.7.2 [temp.over.link]) template-heads and trailing requires-clauses (if any, except as specified in 13.7.5 [temp.friend]).

3. Change in 9.10 [namespace.udecl] 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 declared in `C`, would correspond to (and thus would conflict with) a declaration of a function or function template in C.

4. Change in 12.2.2.3 [over.match.oper] paragraph 4 as follows:

   > A non-template function or function template F named operator== is a rewrite target with first operand o unless `F` is not an implicit object function and search for the name operator!= in the scope S from the instantiation context of the operator expression finds a function or function template `F2` such that would correspond `F2` is an implicit object member function and `F` and `F2` have corresponding non-object signatures (6.4.1 [basic.scope.scope]) to F if its name were operator==, where S is the scope of the class type of o if F is a class member, and the namespace scope of which F is a member otherwise. A function template specialization named operator== is a rewrite target if its function template is a rewrite target.

Additional notes (November, 2024)

Both 9.10 [namespace.udecl] paragraph 11 (see issue 2555) and 11.7.3 [class.virtual] (see issue 2554) as well as the present issue would benefit from defining "corresponds" such that the type of the implicit object parameter (beyond ref-/cv-qualification) is not considered.

Possible resolution:

1. Change in 6.4.1 [basic.scope.scope] paragraph 3 as follows:

   > Two non-static member functions have corresponding object parameters if, considering them to be declared in the same class,
   > - 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 references, are the same, or
   > - their object parameters have the same type.
   > Two non-static member function templates have corresponding object parameters if, considering them to be declared in the same class,
   > - exactly one is an implicit object member function with no ref-qualifier and the types of their object parameters, after removing any references, are equivalent, or
   > - the types of their object parameters are equivalent.
   > Two function templates have corresponding signatures if their template-parameter-lists have the same length, their corresponding template-parameter s are equivalent, they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters.

2. Change in 12.2.2.3 [over.match.oper] paragraph 4 as follows:

   > A non-template function or function template F named operator== is a rewrite target with first operand o unless `F` is not an explicit object member function and search for the name operator!= in the scope S from the instantiation context of the operator expression finds a function or function template `F2` such that would correspond `F2` is not an explicit object member function and `F` and `F2` are corresponding overloads (6.4.1 [basic.scope.scope]) to F if its name were operator==, where S is the scope of the class type of o if F is a class member, and the namespace scope of which F is a member otherwise. A function template specialization named operator== is a rewrite target if its function template is a rewrite target.

This resolution also resolves issue 2555.

#### 2572. Address of overloaded function with no target

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

Consider:

      template <class T> T f(T);   // #1
      template <class T> T* f(T*); // #2
      auto p = &f<int>;

Accoring to 12.3 [over.over] paragraph 3 and 12.3 [over.over] 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 [over.over] 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 [over.over] 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.

Additional notes (July, 2024)

The example in this issue is resolved by issue 2918 by suitably ordering placeholder type deduction before the address operation.

#### 2862. Unclear boundaries of template declarations

Section: 13.1 [temp.pre] Status: review Submitter: Jan Schultke Date: 2024-02-21

(From submission #506.)

Consider:

      template <int>
      struct S {} v;

Is this a declaration of a class template or a declaration of a variable template, or neither? Implementations uniformly reject.

Another example:

      template<class T>
      typedef struct C F;

Proposed resolution (approved by CWG 2024-04-05) [SUPERSEDED]:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

> The declaration in a template-declaration (if any) shall

> - declare or define a function, a class, or a variable, or
> - define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
> - define a member template of a class or class template, or
> - be a deduction-guide, or
> - be an alias-declaration.
> The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains `typedef`.

> A template-declaration is a declaration. If

> - the declaration in a template-declaration introduces an entity using a declarative nested-name-specifier (7.5.5.3 [expr.prim.id.qual]) that is dependent and equivalent to the injected-class-name of a class template or class template partial specialization `C`, and
> - the template-head of the template-declaration is equivalent to that of `C`,
> the template-declaration declares a member of `C`, and the nested-name-specifier is treated as a non-dependent reference to `C` for the purpose of further interpreting the declaration. Otherwise:
> - A class template is introduced by a template-declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or consists solely of an elaborated-type-specifier.
> - A function template is introduced by a template-declaration whose declaration declares a function.
> - An alias template is introduced by a template-declaration whose declaration is an alias-declaration.
> - A variable template is introduced by a template-declaration whose declaration declares a variable.
> [ Example:

>       template<typename T> struct A {
>         template<typename U> struct B;
>         template<typename U> struct B<U*> {
>           template<typename V> void f();
>         };
>       };
>       template<typename T> // #1
>       template<typename U> // #2
>       template<typename V> // #3
>       void A<T>::B<U*>::f() {}

> The template-declaration #1 declares a member of the class template `A`, because `A<T>` is equivalent to the injected-class-name of `A`. The template-declaration #2 declares a member of the class template partial specialization `A<T>::B<U*>`, because `A<T>::B<U*>` is equivalent to the injected-class-name of the partial specialization when `A<T>` is treated as a non-dependent reference to the primary template `A`. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

> [Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one template or member of a template. [ Example:

>     template <int>
>     struct S {} v;    // error: declares both a class template and a variable template
>     template <int>
>     struct V *v;      // OK: declares a variable template

> -- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

> In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]

CWG 2024-06-26

This drafting lacks the required positional association of template-heads with the respective component of the nested-name-specifier.

Possible resolution (rebased on the current Working Draft) [SUPERSEDED]:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

> The declaration in a template-declaration (if any) shall

> - declare or define a function, a class, or a variable, or
> - define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
> - define a member template of a class or class template, or
> - be a friend-type-declaration, or
> - be a deduction-guide, or
> - be an alias-declaration.
> The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains `typedef`.

> A template-declaration is a declaration. If

> - the declaration in a template-declaration introduces an entity using a declarative nested-name-specifier (7.5.5.3 [expr.prim.id.qual]) that is dependent and equivalent to the injected-class-name of a class template or class template partial specialization `C`, and
> - the template-head of the template-declaration is equivalent to that of `C`,
> the template-declaration declares a member of `C`, and the nested-name-specifier is treated as a non-dependent reference to `C` for the purpose of further interpreting the declaration. Otherwise:
> - A class template is introduced by a template-declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or consists solely of an elaborated-type-specifier.
> - A function template is introduced by a template-declaration whose declaration declares a function.
> - An alias template is introduced by a template-declaration whose declaration is an alias-declaration.
> - A variable template is introduced by a template-declaration whose declaration declares a variable.
> [ Example:

>       template<typename T> struct A {
>         template<typename U> struct B;
>         template<typename U> struct B<U*> {
>           template<typename V> void f();
>         };
>       };
>       template<typename T> // #1
>       template<typename U> // #2
>       template<typename V> // #3
>       void A<T>::B<U*>::f() {}

> The template-declaration #1 declares a member of the class template `A`, because `A<T>` is equivalent to the injected-class-name of `A`. The template-declaration #2 declares a member of the class template partial specialization `A<T>::B<U*>`, because `A<T>::B<U*>` is equivalent to the injected-class-name of the partial specialization when `A<T>` is treated as a non-dependent reference to the primary template `A`. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

> [Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one templated entity (not considering declarations inhabiting scopes nested therein). [ Example:

>     template <int>
>     struct S {} v;    // error: declares both a class template and a variable template
>     template <int>
>     struct V *v;      // OK, declares a variable template

> -- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

> In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]

Additional notes (December, 2024)

Subclause 7.5.5.3 [expr.prim.id.qual] paragraph 3 already interprets declarative nested-name-specifiers.

Possible resolution:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

> The declaration in a template-declaration (if any) shall

> - declare or define a function, a class, or a variable, or
> - define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
> - define a member template of a class or class template, or
> - be a friend-type-declaration, or
> - be a deduction-guide, or
> - be an alias-declaration.
> The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains `typedef`.

> A template-declaration is a declaration. If the declaration in a template-declaration introduces an entity using a declarative nested-name-specifier that nominates a primary class template or class template partial specialization `C` (7.5.5.3 [expr.prim.id.qual]), the template-declaration declares a member of `C`, and the nested-name-specifier is treated as a non-dependent reference to `C` for the purpose of further interpreting the declaration. Otherwise:

> - A class template is introduced by a template-declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or consists solely of an elaborated-type-specifier.
> - A function template is introduced by a template-declaration whose declaration declares a function.
> - An alias template is introduced by a template-declaration whose declaration is an alias-declaration.
> - A variable template is introduced by a template-declaration whose declaration declares a variable.
> [ Example:

>       template<typename T> struct A {
>         template<typename U> struct B;
>         template<typename U> struct B<U*> {
>           template<typename V> void f();
>         };
>       };
>       template<typename T> // #1
>       template<typename U> // #2
>       template<typename V> // #3
>       void A<T>::B<U*>::f() {}

> The template-declaration #1 declares a member of the class template `A`, because `A<T>` nominates the primary template `A`. The template-declaration #2 declares a member of the class template partial specialization `A<T>::B<U*>`, because `A<T>::B<U*>` nominates the class template partial specialization. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

> [Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one templated entity (not considering declarations inhabiting scopes nested therein). [ Example:

>     template <int>
>     struct S {} v;    // error: declares both a class template and a variable template
>     template <int>
>     struct V *v;      // OK, declares a variable template

> -- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

> In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]

#### 2917. Disallow multiple friend-type-specifiers for a friend template

Section: 13.1 [temp.pre] Status: review Submitter: Ambrose T Date: 2024-07-30 Liaison: EWG

(From submissions #586 and #593.)

Consider:

    struct C {
      template <typename>
      friend class Foo, int;
    };

This is obviously nonsense, but is not prohibited by the wording.

Also consider:

      struct S {
        template <typename T>
        friend class Foo<T>::Nested;       // OK; see 13.7.5 [temp.friend] paragraph 5

        template <typename ...Ts>
        friend class Foo<Ts>::Nested...;   // makes no sense
      };

Suggested resolution (2024-07-30):

Change in 13.1 [temp.pre] paragraph 5 as follows:

> In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. When such a declaration is used to declare a class template, no declarator is permitted. In a template-declaration whose declaration is a friend-type-declaration, the friend-type-specifier-list shall consist of exactly one friend-type-specifier; if it is a pack expansion (13.7.4 [temp.variadic]), any packs expanded by that pack expansion shall not have been introduced by the template-declaration. [ Example:

>     template<class ...>
>     struct C {
>       struct Nested { };
>     };

>     template<class ... Us>
>     struct S {
>       template <typename ...Ts>
>       friend class C<Ts>::Nested...;     // error
>       friend class C<Us>::Nested...;     // OK
>     };

> -- end example ]

The above resolution allows the following example:

      template<class ... Us>
      struct S {
       template<class T>
       friend class C<T, Us>::Nested...;
      };

CWG was not convinced the above example was intended to be supported by paper P2893R3, which introduced pack expansions for friend declarations.

Proposed resolution (2024-08-16):

(This is not a DR.)

(Issue 2862 modifies the same paragraph.)

Change in 13.1 [temp.pre] paragraph 5 as follows:

> In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. When such a declaration is used to declare a class template, no declarator is permitted. In a template-declaration whose declaration is a friend-type-declaration, the friend-type-specifier-list shall consist of exactly one friend-type-specifier that is not a pack expansion. [ Example:

>     template<class ...>
>     struct C {
>       struct Nested { };
>     };

>     template<class ... Us>
>     struct S {
>       template <typename ...Ts>
>       friend class C<Ts>::Nested...;     // error
>       friend class C<Us>::Nested...;     // OK
>     };

> -- end example ]

CWG 2024-08-16

The proposed resolution above disallows a few examples from paper P2893R3 (Variadic friends), such as

      template<class... Ts>
      struct VS {
        template<class U>
        friend class C<Ts>::Nested...; // now ill-formed
      };

The adopted wording for P2893R3 makes the friend-type-specifier (not the entire template-declaration) the pattern that is expanded by the pack expansion, leading to the expansion

      struct VS<T1, T2, T3> {
        template<class U>
        friend class C<T1>::Nested, class C<T2>::Nested, class C<T3>::Nested;
      };

However, that violates the principle that a template-declaration declares exactly one entity (see issue 2862).

As an aside, the paper as adopted misrepresents the status of members of dependent types, which are covered by 13.7.5 [temp.friend] paragraph 5.

CWG welcomes a paper making the template friend cases valid, but such a facility would appear to require substantial changes to the normative wording, which a core issue is not equipped for.

CWG asks EWG to consent to the reduction in scope for the variadic friends facility, via paper issue cplusplus/papers#2032.

CWG 2025-06-16

EWG agrees with the reduction in scope for the variadic friends facility, as proposed above.

#### 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 S {
      template<typename> void f();
    };

    template<typename = int> void S::f() { }   // ok?

There is implementation divergence in the treatment of this example. The relevant wording appears to be 13.2 [temp.param] 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.,

    template<typename> void f();
    template<typename = int> void f() { }

which is explicitly permitted.

Proposed resolution:

Change in 13.2 [temp.param] 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:

      template<class T> concept ctible = requires { T(); };

      class A {
        template <class T> friend struct B;
        A();
      };

      template <class T> struct B;
      template <ctible T> struct B<T> { T t; };
      B<A> b;  // #1

      template <class T> struct C { };
      template <ctible T> struct C<T> { T t; };
      C<A> c;  // #2

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 [temp.constr.atomic] 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 [SUPERSEDED]:

Change in 13.5.2.3 [temp.constr.atomic] 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. ...

Additional notes (December, 2024)

Reflector review yielded an improved resolution.

Proposed resolution [SUPERSEDED]:

Append a new paragraph after 11.8.1 [class.access.general] paragraph 9:

> Access for a default template-argument (13.2 [temp.param]) is checked in the context in which it appears rather than at any points of use of it. [Example 5: ... —end example]

> Access for an atomic constraint is checked in the context in which its expression appears, or for an immediately-declared constraint, in the context where its type-constraint appears, rather than at any point where its satisfaction is checked. [ Example:

>       template<class T> concept ctible = requires { T(); };

>       class A {
>         template<class T> friend struct B;
>         A();
>       };

>       template<class T> struct B;
>       template<ctible T> struct B<T> { T t; };
>       B<A> b;                    // error: uses incomplete primary template of B

>       template<class T> struct C { };
>       template<ctible T> struct C<T> { T t; };
>       C<A> c;                    // OK, not using the partial specialization

>       template<typename T, bool b> concept C = b;
>       class U { static constexpr bool enabled = true; friend struct X; };
>       struct X {
>        template<typename U, C<U::enabled> V> struct A {};
>       };
>       struct Y {
>        template<typename U, C<U::enabled> V> struct A {};
>       };
>       X::A<U, int> x;    // OK
>       Y::A<U, int> y;    // error: cannot access U::enabled

> --end example ]

Additional notes (December, 2024)

The resolution was amended.

Proposed resolution [SUPERSEDED]:

Append a new paragraph after 11.8.1 [class.access.general] paragraph 9:

> Access for a default template-argument (13.2 [temp.param]) is checked in the context in which it appears rather than at any points of use of it. [Example 5: ... —end example]

> Access for an atomic constraint is checked in a context unrelated to any class or function. [ Example:

>       template<bool B> concept C = B;

>       struct A {
>         template<typename T> void f() requires C<T::value>;
>       };
>       struct B : A {
>         using A::f;
>         template<typename T> void f() requires C<T::value> && true;
>       };

>       class X { static constexpr bool value = true; friend struct A; };
>       void g() {
>         B().f<X>();    // error: no viable function
>       }

> --end example ]

Additional notes (January, 2025)

See also issue 2789, but that issue applies to non-templates only.

Possible resolution:

Append a new paragraph after 11.8.1 [class.access.general] paragraph 9:

> Access for a default template-argument (13.2 [temp.param]) is checked in the context in which it appears rather than at any points of use of it. [Example 5: ... —end example]

> Access for a parameter mapping is checked in a context unrelated to any class or function. [ Example:

>       template<bool B> concept C = B;

>       struct A {
>         template<typename T> void f() requires C<T::value>;
>       };
>       struct B : A {
>         using A::f;
>         template<typename T> void f() requires C<T::value> && true;
>       };

>       class X { static constexpr bool value = true; friend struct A; };
>       void g() {
>         B().f<X>();    // error: no viable function
>       }

> --end example ]

#### 2916. Variable template partial specializations should not be declared `static`

Section: 13.7.6 [temp.spec.partial] Status: review Submitter: Richard Smith Date: 2024-07-24

(From submission #580.)

There appears to be no prohibition on applying the `static` storage class specifier to a variable template partial specialization. However, 6.7 [basic.link] clearly says that the name of the template has linkage (not its specializations). Implementations give specializations instantiated from a `static` partial specialization internal linkage even if the template has external linkage. This is undesirable.

Proposed resolution (approved by CWG 2024-08-16): [SUPERSEDED]

Add before 13.7.6.1 [temp.spec.partial.general] paragraph 10 as follows:

> A partial specialization declaration for a variable template shall not have the storage-class-specifier `static`.

> The usual access checking rules do not apply to non-dependent names used to specify template arguments of the simple-template-id of the partial specialization. ...

Additional notes (August, 2024)

If the primary template is declared `static`, the least breakage of existing code occurs if the partial specialization is required to also be declared `static`.

Possible resolution:

Add before 13.7.6.1 [temp.spec.partial.general] paragraph 10 as follows:

> A partial specialization declaration for a variable template shall give its name the same linkage as the declaration of the primary template. A partial specialization declaration for a static data member template shall have the storage-class-specifier `static`.

> The usual access checking rules do not apply to non-dependent names used to specify template arguments of the simple-template-id of the partial specialization. ...

#### 2834. Partial ordering and explicit object parameters

Section: 13.7.7.3 [temp.func.order] Status: review Submitter: Jason Merrill Date: 2023-12-02

Consider:

      template <typename T>
      struct A {
        T f(this auto, auto);   // #1
        int f(auto);            // #2
      };
      int main() {
        A<int>().f(42);
      }

There is implementation divergence in the handling of this example: Clang and MSVC consider the example ambiguous, EDG chooses #1.

Proposed resolution (reviewed by CWG 2024-01-19):

Change in 13.7.7.3 [temp.func.order] paragraph 3 as follows:

> Each function template M that is a member function with no explicit object parameter is considered to have a new first parameter of type X(M), described below, inserted in its function parameter list. If exactly one of the function templates was considered by overload resolution via a rewritten candidate (12.2.2.3 [over.match.oper]) with a reversed order of parameters, then the order of the function parameters in its transformed template is reversed. For a function template M with cv-qualifiers cv that is a non-static member of a class A:
> - The type X(M) is “rvalue reference to cv A” if the optional ref-qualifier of M is && or if M has no ref-qualifier and the positionally-corresponding parameter of the other transformed template has rvalue reference type; if this determination depends recursively upon whether X(M) is an rvalue reference type, it is not considered to have rvalue reference type.
> - Otherwise, X(M) is “lvalue reference to cv A”.
> For a function template M that is a static member of a class A:
> - The type X(M) is the type of the object parameter of the other transformed template, if any.
> - Otherwise, no new first parameter is inserted.
> [Note 2: This allows a non-static member to be ordered with respect to a static or non-member function and for the results to be equivalent to the ordering of two equivalent non-members. —end note]

CWG 2024-01-19

It seems #2 should be chosen, because it is more specialized than #1.

#### 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 [temp.inst] paragraph 11 does say,

> Implicitly instantiated class and function template specializations are placed in the namespace where the template is defined.

which could be read as implying that the specialization has the same linkage as the template itself. Implementation practice seems to be that the weakst linkage of the template and the arguments is used for the specialization.

Additional notes (February, 2023)

Template specializations do not have linkage.

#### 2808. Explicit specialization of defaulted special member function

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

Consider:

      template<typename T> struct S {
        S(const S&) = default;
        S& operator=(const S&) = default;
        int n;
      };

      // Are the explicit specializations OK?
      template<> S<int>::S(const S&) noexcept { }
      template<> S<int>& S<int>::operator=(const S& other) noexcept {
        return *this;
      }

If the explicit specialization were allowed, would the answer to `std::is_trivially_copyable<S<int>>` change? What is `decltype(&S::operator=)` (the defaulted definition is `noexcept`, yet no instantiation or implicit definition is triggered)?

Proposed resolution (2023-10-20) [SUPERSEDED]:

Change in 13.9.2 [temp.inst] paragraph 3 and add bullets as follows:

> The implicit instantiation of a class template specialization causes
> - the implicit instantiation of the declarations, but not of the definitions, of the non-deleted user-provided class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
> - the implicit instantiation of the definitions of
>   - defaulted and deleted member functions as defaulted (9.6.2 [dcl.fct.def.default]) or deleted (9.6.3 [dcl.fct.def.delete]),
>   - unscoped member enumerations, and
>   - member anonymous unions.
>   [ Note: The implicit instantiation determines whether a defaulted function is deleted, but a non-deleted defaulted function is implicitly defined only when it is odr-used or needed for constant evaluation. -- end note ]
> The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions.

Additional notes (October, 2023)

It is desirable to require a diagnostic for such attempted explicit specializations.

Furthermore, there are situations where the "potentially-throwing" property of a non-deleted function is queried:

      template<int...>
      struct C {
        // This class has no eligible copy assignment operator at all.
        void operator=(const C&) requires false;
        void operator=(int) noexcept;
        operator int() const;
      };

      void f(C<> &c) {
        c = c;        // Convert to int, then construct from int.
      }

      struct D {
        C<> c;
      };

      bool g(D d) {
        return noexcept(d = d);     // #1. If this is valid, what does it return?
      }

      struct E {
        C<> c;
        E &operator=(const E &o) { c = o.c; }
      };

All major implementations agree that `E` is valid. However, clang, gcc, and EDG delete the copy assignment operator of `D`, thus sidestepping the question at #1. (MSVC accepts, but #1 returns true despite the potentially-throwing conversion to `int`.) Apparently, user-defined conversions for the first argument of `C`'s assignment operator are ignored in implementations other than MSVC. However, the specification is silent on that.

Possible resolution:

1. Change in 13.9.2 [temp.inst] paragraph 3 and add bullets as follows:

   > The implicit instantiation of a class template specialization causes
   > - the implicit instantiation of the declarations, but not of the definitions, of the non-deleted user-provided class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
   > - the implicit instantiation of the definitions of
   >   - defaulted and deleted member functions as defaulted (9.6.2 [dcl.fct.def.default]) or deleted (9.6.3 [dcl.fct.def.delete]),
   >   - unscoped member enumerations, and
   >   - member anonymous unions.
   >   [ Note: The implicit instantiation determines whether a defaulted function is deleted, but a non-deleted defaulted function is implicitly defined only when it is odr-used or needed for constant evaluation. -- end note ]
   > The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions.

2. Change in 13.9.4 [temp.expl.spec] paragraph 7 as follows:

   > If a template, a member template or a member of a templated class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required unless the specialization is for a member of a templated class whose definition is implicitly instantiated as a result of the implicit instantiation of the class (13.9.2 [temp.inst]). ...

#### 2847. Constrained explicit specializations of function templates at class scope

Section: 13.9.4 [temp.expl.spec] Status: review Submitter: Krystian Stasiowski Date: 2023-12-15

(From submission #482.)

Consider:

      template<typename T>
      concept C = sizeof(T) > sizeof(char);
      template<typename T>
      concept D = sizeof(T) > sizeof(int) ;

      template<typename T>
      struct A
      {
        template<typename U>
        constexpr int f(U) requires C<U> { return 0; }

        template<>
        constexpr int f(int) requires D<T> { return 1; }
      };

      static_assert(A<int>().f(0) == 0);   // #1

There is substantial implementation variance: GCC does not allow explicit specializations of function templates at class scope (contrary to the rule change introduced by issue 727), clang rejects them if a trailing-requires-clause is present, and EDG accepts, but ignores the constraint, causing #1 to fail.

Proposed resolution (reviewed by CWG 2024-03-01) [SUPERSEDED]:

1. Add a new paragraph before 13.9.4 [temp.expl.spec] paragraph 8 as follows:

   > An explicit specialization of a function shall not have a trailing requires-clause (9.3.1 [dcl.decl.general]). [ Example:

   >       template<typename T>
   >       concept C = sizeof(T) <= sizeof(int);

   >       template<typename T>
   >       struct A {
   >         template<typename U>
   >         void f(U) requires C<U>;

   >         template<>
   >         void f(char);    // OK

   >         template<>
   >         void f(short) requires (sizeof(T) >= 1); // error: trailing requires-clause not allowed
   >       };

   >       template<>
   >       template<typename U>
   >       void A<int>::f(U) requires C<U> {} // OK, explicit specialization is a template

   > -- end example ]

   > The placement of explicit specialization declarations for function templates ...

2. Add another example at the end of 13.9.4 [temp.expl.spec] paragraph 15 as follows:

   > [ Example:

   >        template<typename T>
   >        struct D {
   >          template<typename U>
   >          static constexpr int f(U);          // #1

   >          template<typename U>
   >          static constexpr int f(U) requires (sizeof(T) == 1);    // #2

   >          template<>
   >          constexpr int f(int)                // #3
   >          { return 1; }
   >        };

   >        template<> template<typename U>
   >        constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1)  // #4
   >        { return 0; }

   >        static_assert(D<char>::f(0) == 1);          // overload resolution selects #2; #3 is a specialization for #2
   >        static_assert(D<char[2]>::f(0) == 1);       // overload resolution selects #1; #3 is a specialization for #1
   >        static_assert(D<signed char>::f(0) == 1);   // overload resolution selects #2; #3 is a specialization for #2
   >        static_assert(D<signed char>::f(0.0) == 0); // overload resolution selects #2; #4 is a specialization for #2

   > -- end example ]

Additional notes (April, 2024)

The phrasing "an explicit specialization of a function" does not make sense.

Possible resolution:

1. Add a new paragraph before 13.9.4 [temp.expl.spec] paragraph 8 as follows:

   > An explicit specialization that declares a function shall not have a trailing requires-clause (9.3.1 [dcl.decl.general]). [ Example:

   >       template<typename T>
   >       concept C = sizeof(T) <= sizeof(int);

   >       template<typename T>
   >       struct A {
   >         template<typename U>
   >         void f(U) requires C<U>;

   >         template<>
   >         void f(char);    // OK

   >         template<>
   >         void f(short) requires (sizeof(T) >= 1); // error: trailing requires-clause not allowed
   >       };

   >       template<>
   >       template<typename U>
   >       void A<int>::f(U) requires C<U> {} // OK, explicit specialization is a template

   >       template<>
   >       template<>
   >       void A<short>::f(int) requires C<int> {} // error: trailing requires-clause for a declaration of a non-templated function

   > -- end example ]

   > The placement of explicit specialization declarations for function templates ...

2. Add another example at the end of 13.9.4 [temp.expl.spec] paragraph 15 as follows:

   > [ Example:

   >        template<typename T>
   >        struct D {
   >          template<typename U>
   >          static constexpr int f(U);          // #1

   >          template<typename U>
   >          static constexpr int f(U) requires (sizeof(T) == 1);    // #2

   >          template<>
   >          constexpr int f(int)                // #3
   >          { return 1; }
   >        };

   >        template<> template<typename U>
   >        constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1)  // #4
   >        { return 0; }

   >        static_assert(D<char>::f(0) == 1);          // overload resolution selects #2; #3 is a specialization for #2
   >        static_assert(D<char[2]>::f(0) == 1);       // overload resolution selects #1; #3 is a specialization for #1
   >        static_assert(D<signed char>::f(0) == 1);   // overload resolution selects #2; #3 is a specialization for #2
   >        static_assert(D<signed char>::f(0.0) == 0); // overload resolution selects #2; #4 is a specialization for #2

   > -- end example ]

#### 2896. Template argument deduction involving exception specifications

Section: 13.10.3 [temp.deduct] Status: review Submitter: Krystian Stasiowski Date: 2024-05-15

(From submission #537.)

Core issue 2355 added support for deducing the exception specification of a function template. However, implementations uniformly reject the following example:

      template<bool B>
      void f() noexcept(B);   // #1

      template<>
      void f() noexcept;      // explicit specialization of #1?

This is arguably the correct approach, because exception specifications are instantiated separately and do not cause substitution failure in the immediate context (13.10.3.1 [temp.deduct.general]). For class-scope explicit specializations, the noexcept-specifier has not even been parsed, yet. If we exclude such deduction from function declarations, we also need to avoid considering the exception specification for partial ordering, otherwise partial ordering would always fail.

A similar consideration applies to the address-of-function-template case:

      struct A {
        static constexpr bool x = true;
      };

      template<typename T, typename U>
      void f(T, U*) noexcept(T::x);   // #1

      template<typename T, typename U>
      void f(T, U) noexcept(T::y);    // #2

      void(&g)(A, int*) noexcept = f;  // selects #1; not a hard error during deduction for #2

Suggested resolution [SUPERSEDED]:

1. Change in 13.10.3.7 [temp.deduct.decl] paragraph 1 as follows:

   > ... In all these cases, P is the function type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 7.6.2.8 [expr.new] , in all cases ignoring the noexcept-specifier (if any) of the function types. The deduction is done as described in 13.10.3.6 [temp.deduct.type].

2. Change in 13.10.3.5 [temp.deduct.partial] paragraph 3 as follows:

   > The types used to determine the ordering depend on the context in which the partial ordering is done:
   > - In the context of a function call, the types used are those function parameter types for which the function call has arguments. [ Footnote: ... ]
   > - In the context of a call to a conversion function, the return types of the conversion function templates are used.
   > - In other contexts (13.7.7.3 [temp.func.order]), the function template's function type outside of the noexcept-specifier is used.

3. Change in 13.10.3.3 [temp.deduct.funcaddr] paragraph 1 as follows:

   > Template arguments can be deduced from the type specified when taking the address of an overload set (12.3 [over.over]). If there is a target, the function template's function type and the target type are used as the types of P and A, ignoring the noexcept-specifier (if any) of both function types, and the deduction is done as described in 13.10.3.6 [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A.

CWG 2024-06-14

For the last case (address of overload set), the following example is currently well-formed, but is rendered ill-formed by the suggested resolution:

      void f(T*);           // #1
      void f(T) noexcept;   // #2
      void (&g)(int*) noexcept = f;   // status quo: selects #2; suggested resolution: selects #1 (more specialized) and fails the initialization

Possible resolution:

1. Change in 12.3 [over.over] paragraph 5 as follows:

   > All functions with associated constraints that are not satisfied (13.5.3 [temp.constr.decl]) are eliminated from the set of selected functions. Any function template specialization whose exception specification is originally non-dependent is eliminated from the set unless the function type of the specialization (after possibly applying the function pointer conversion (7.3.14 [conv.fctptr])) is identical to the function type of the target type. If more than one function in the set remains, all function template specializations in the set are eliminated if the set also contains a function that is not a function template specialization. Any given non-template function F0 is eliminated if the set contains a second non-template function that is more constrained than F0 according to the partial ordering rules of 13.5.5 [temp.constr.order]. 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.

2. Change in 13.10.3.7 [temp.deduct.decl] paragraph 1 as follows:

   > ... In all these cases, P is the function type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 7.6.2.8 [expr.new] , in all cases ignoring the noexcept-specifier (if any) of the function types. The deduction is done as described in 13.10.3.6 [temp.deduct.type].

3. Change in 13.10.3.5 [temp.deduct.partial] paragraph 3 as follows:

   > The types used to determine the ordering depend on the context in which the partial ordering is done:
   > - In the context of a function call, the types used are those function parameter types for which the function call has arguments. [ Footnote: ... ]
   > - In the context of a call to a conversion function, the return types of the conversion function templates are used.
   > - In other contexts (13.7.7.3 [temp.func.order]), the function template's function type outside of the noexcept-specifier is used.

4. Change in 13.10.3.3 [temp.deduct.funcaddr] paragraph 1 as follows:

   > Template arguments can be deduced from the type specified when taking the address of an overload set (12.3 [over.over]). If there is a target, the function template's function type and the target type are used as the types of P and A, ignoring the noexcept-specifier (if any) of both function types, and the deduction is done as described in 13.10.3.6 [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A.

#### 2875. Missing support for round-tripping null pointer values through indirection/address operators

Section: C.7.4 [diff.expr] Status: review Submitter: Richard Smith Date: 2024-03-21

C supports the following, C++ does not (see issues 232 and 2823):

      void f() {
        char *p = 0;
        char *p2 = &*p;       // OK in C, undefined behavior in C++
        int a[5];
        int *q = &a[5];       // OK in C, undefined behavior in C++
      }

This incompatibility should be documented in Annex C.

Proposed resolution (approved by CWG 2024-06-26):

Add a new paragraph to C.7.4 [diff.expr] as follows:

> Affected subclause: 7.6.2.2 [expr.unary.op]
> Change: In certain contexts, taking the address of a dereferenced null or past-the-end pointer value is well-defined in C (and yields the original pointer value), but results in undefined behavior in C++. For example:

>       void f() {
>         char *p = 0;
>         char *p2 = &*p;   // well-defined in C, undefined behavior in C++
>         char *p3 = &p[0]; // well-defined in C, undefined behavior in C++
>         int a[5];
>         int *q = &a[5];   // well-defined in C, undefined behavior in C++
>       }

> Rationale: Consistent treatment of lvalues in C++.
> Effect on original feature: Well-formed and well-defined C code exhibits undefined behavior in C++.
> Difficulty of converting: Syntactic transformation to pointer arithmetic and possible addition of a check for null pointer values.
> How widely used: Occasionally.

CWG 2024-06-26

Implementations are required to diagnose undefined behavior in constant expressions. The issue is kept in review status to allow time for submitting a paper to EWG to make the `&a[5]` case well-defined. See also C23 6.5.3.2p3.

### Issues with "Drafting" Status

#### 2632. 'user-declared' is not defined

Section: Clause 3 [intro.defs] Status: drafting Submitter: Anoop Rana Date: 2022-09-07

The term "user-declared" is used 30 times throughout the standard, but it is not defined.

Proposed resolution:

Add a new entry after 3.66 [defns.unspecified] as follows:

> user-declared [defns.user.declared]

> not implicitly declared

CWG 2024-06-14

The proposed resolution does not work, because utterances such as "a default constructor is implicitly declared if no constructor is user-declared" become circular. The eventual definition should address the question whether constructors inherited from a base class are considered user-declared in the inheriting class.

#### 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 [basic.pre] 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:

      export module A;
      enum class X { x };
      enum Y { y };

      export module B;
      import A;
      export using XB = X;
      export using YB = Y;

      // client code
      import B;
      int main() {
        XB x = XB::x; // should be OK because definition of X is reachable, even
                      // though A is not imported
        YB y = YB::y; // similarly OK
        YB z = ::y;   // error, because y from module A is not visible
      }

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

#### 2926. Lookup context for dependent qualified names

Section: 6.5.5.1 [basic.lookup.qual.general] Status: drafting Submitter: Brian Bi Date: 2024-08-16

(From submission #599.)

Consider:

      template <typename U>
      constexpr bool foo = U::b;

      template <class T>
      struct A {
        A() requires(foo<A>) {}
        static constexpr bool b = true;
      };
      A<int> a;

All implementation accept, but there is no normative wording prescribing the lookup context for `U::b`.

Proposed resolution (approved by CWG 2024-09-13):

Change in 6.5.5.1 [basic.lookup.qual.general] paragraph 3 as follows:

> Qualified name lookup in a class, namespace, or enumeration performs a search of the scope associated with it (6.5.2 [class.member.lookup]) except as specified below. Unless otherwise specified, a qualified name undergoes qualified name lookup in its lookup context from the point where it appears unless the lookup context either
> - is dependent and is not the current instantiation (13.8.3.2 [temp.dep.type]), in which case the lookup is done from the point of instantiation (13.8.4.1 [temp.point]), or
> - is not a class or class template a type that is not a class, in which case unqualified lookup is performed from the point where it appears. [ Note: Such a qualified name can be used to name a destructor (7.5.5.5 [expr.prim.id.dtor]). -- end note ]
> If nothing is found by qualified lookup for a member-qualified name that is the terminal name (7.5.5.2 [expr.prim.id.unqual]) of a nested-name-specifier and is not dependent, it undergoes unqualified lookup.

Additional notes (September, 2024)

The proposed wording appears to make the following example unintentionally well-formed:

      int n;
      using T = int;
      int m = T::n;

CWG 2024-11-08

The example above is already ill-formed per 6.5.5.1 [basic.lookup.qual.general] paragraph 1.

#### 2324. Size of base class subobject

Section: 6.8.2 [intro.object] Status: drafting Submitter: GB Date: 2017-02-27
P0488R0 comment GB 9

According to 6.8.2 [intro.object] 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.8.2 [intro.object] Status: drafting Submitter: CA Date: 2017-02-27
P0488R0 comment CA 12

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

      #include <new>
      int bar() {
        alignas(int) unsigned char space[sizeof(int)];
        int *pi = new (static_cast<void *>(space)) int;
        *pi = 42;
        return [=]() mutable {
          return   *std::launder(reinterpret_cast<int *>(space)); }();
       }

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.8.2 [intro.object] 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 [basic.lval] paragraph 8.

#### 2469. Implicit object creation vs constant expressions

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

It is not intended that implicit object creation, as described in 6.8.2 [intro.object] 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 issue 2464, 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.

#### 3029. Confusing note about ordinary character types for aligned memory areas

Section: 6.8.3 [basic.align] Status: drafting Submitter: Xavier Bonaventura Date: 2025-03-20

(From submission #687.)

Subclause 6.8.3 [basic.align] paragraph 6 specifies:

> The alignment requirement of a complete type can be queried using an alignof expression (7.6.2.6 [expr.alignof]). Furthermore, the narrow character types (6.9.2 [basic.fundamental]) shall have the weakest alignment requirement. [Note 2: This enables the ordinary character types to be used as the underlying type for an aligned memory area (9.13.2 [dcl.align]). —end note]

The note has multiple problems: First, the note talks about "ordinary character types" (including `char8_t`; see 6.9.2 [basic.fundamental] paragraph 7), whereas the immediately preceding sentence is limited to "narrow character types". Second, due to memory model concerns, `signed char` is not suitable for aligned memory areas providing storage for other objects.

Proposed resolution (no objections during CWG reflector review starting 2025-05-11):

Change in 6.8.3 [basic.align] paragraph 6 as follows:

> The alignment requirement of a complete type can be queried using an alignof expression (7.6.2.6 [expr.alignof]). Furthermore, the narrow character types (6.9.2 [basic.fundamental]) shall have the weakest alignment requirement. [Note 2: This enables the ordinary character types to The type `unsigned char` can be used as the underlying type for an aligned memory area (9.13.2 [dcl.align]). —end note]

Additional notes (June, 2025)

The "underlying type" is for enumerations and derived integer types and thus is the wrong phrase to use here.

#### 1530. Member access in out-of-lifetime objects

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

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

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

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

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

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

Additional note (January, 2013):

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

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

For example:

      struct DerivedMember { ... };

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

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

      Derived a;

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

Additional note (March, 2013):

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

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

#### 2863. Unclear synchronization requirements for object lifetime rules

Section: 6.8.4 [basic.life] Status: drafting Submitter: Richard Smith Date: 2024-02-24

(From submission #507.)

Subclause 6.8.4 [basic.life] paragraph 6 specifies:

> Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [ Footnote: ... ] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, ...

"Before" and "after" are intended to refer to the "happens before" relation (which is not a total order), but the specific use here does not properly handle evaluations racing with the start of the lifetime of an object.

Possible resolution (option 1) [SUPERSEDED]:

1. Change in 6.8.4 [basic.life] paragraph 4 as follows:

   > The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime. [Note 2: In particular, before the lifetime of an object starts and after its lifetime ends there are significant restrictions on the use of the an object not within its lifetime, as described below, in 11.9.3 [class.base.init], and in 11.9.5 [class.cdtor]. Also, the behavior of an object under construction and destruction can differ from the behavior of an object whose lifetime has started and not ended. 11.9.3 [class.base.init] and 11.9.5 [class.cdtor] describe the behavior of an object during its periods of construction and destruction. —end note]

2. Change in 6.8.4 [basic.life] paragraph 6 as follows:

   > Before the lifetime of an object has started but In an evaluation that occurs after the storage which the an object will occupy has been allocated [ Footnote: ... ] but not after the lifetime of the object has started or, in an evaluation that occurs not before 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]. ...

3. Change in 6.8.4 [basic.life] paragraph 7 as follows:

   > Similarly, before the lifetime of an object has started but in an evaluation that occurs after the storage which the an object will occupy has been allocated but not after the lifetime of the object has started or, in an evaluation that occurs not before 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]. ...

4. Change in 6.8.4 [basic.life] paragraph 8 as follows:

   > If, in an evaluation that occurs not before 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 original object is transparently replaceable (see below) by the new object. ...

Proposed resolution (approved by CWG 2024-04-19) [SUPERSEDED]:

1. Change in 6.8.4 [basic.life] paragraph 4 as follows:

   > The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime. [Note 2: In particular, before the lifetime of an object starts and after its lifetime ends there are significant restrictions on the use of the an object not within its lifetime, as described below, in 11.9.3 [class.base.init], and in 11.9.5 [class.cdtor]. Also, the behavior of an object under construction and destruction can differ from the behavior of an object whose lifetime has started and not ended. 11.9.3 [class.base.init] and 11.9.5 [class.cdtor] describe the behavior of an object during its periods of construction and destruction. —end note]

2. Add a new paragraph before 6.8.4 [basic.life] paragraph 6 as follows:

   > An evaluation E is said to be in a storage-only phase of an object O if
   > - E happens after the storage which O will occupy is allocated and E does not happen after the lifetime of O starts; or
   > - E does not happen before the lifetime of O ends and E happens before the storage which O occupied is reused or released.

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

   > Before the lifetime of an object has started but after the storage which theobject will occupy has been allocated [ Footnote: ... ] but not after the lifetime of the object has started or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, In an evaluation that is in a storage-only phase of an object, 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]. ...

4. Change in 6.8.4 [basic.life] paragraph 7 as follows:

   > 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, in an evaluation that is in a storage-only phase of an object, 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]. ...

5. Change in 6.8.4 [basic.life] paragraph 8 as follows:

   > If, in an evaluation that is in a storage-only phase of an object and where the evaluation does not happen before after the lifetime of an the object ends 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 original object is transparently replaceable (see below) by the new object. ...

Additional notes (April, 2024)

Concerning the proposed change to 6.8.4 [basic.life] paragraph 8, it was noted that (for example) a common non-allocating placement new does not happen before the storage is reused.

#### 1676. `auto` return type for allocation and deallocation functions

Section: 6.8.6.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 issue 1669.)

#### 2042. Exceptions and deallocation functions

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

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

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

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

#### 2826. Missing definition of "temporary expression"

Section: 6.8.7 [class.temporary] Status: drafting Submitter: Brian Bi Date: 2022-12-16

The term "temporary expression" is used in 11.9.3 [class.base.init] paragraph 8 and 11.9.3 [class.base.init] paragraph 11, but is never defined.

Proposed resolution (approved by CWG 2023-11-10):

1. Change in 6.8.7 [class.temporary] paragraph 6 as follows:

   > The third context is when a reference binds to a temporary object. [ Footnote: ... ] The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was A temporary expression is a glvalue obtained through one of the following:
   > - a temporary materialization conversion (7.3.5 [conv.rval]),
   > - `( expression )`, where expression is one of these expressions a temporary expression,
   > - subscripting (7.6.1.2 [expr.sub]) of an array operand, where that operand is one of these expressions a temporary expression,
   > - a class member access (7.6.1.5 [expr.ref]) using the . operator where the left operand is one of these expressions a temporary expression and the right operand designates a non-static data member of non-reference type,
   > - a pointer-to-member operation (7.6.4 [expr.mptr.oper]) using the .* operator where the left operand is one of these expressions a temporary expression and the right operand is a pointer to data member of non-reference type,
   > - a
   >   - const_cast (7.6.1.11 [expr.const.cast]),
   >   - static_cast (7.6.1.9 [expr.static.cast])
   >   - dynamic_cast (7.6.1.7 [expr.dynamic.cast]), or
   >   - reinterpret_cast (7.6.1.10 [expr.reinterpret.cast])
   >   converting, without a user-defined conversion, a glvalue operand that is one of these expressions a temporary expression to a glvalue that refers to the object designated by the operand, or to its complete object or a subobject thereof,
   > - a conditional expression (7.6.16 [expr.cond]) that is a glvalue where the second or third operand is one of these expressions a temporary expression, or
   > - a comma expression (7.6.20 [expr.comma]) that is a glvalue where the right operand is one of these expressions a temporary expression.
   > If a reference is bound to an object O that is the result of a temporary expression, the complete object of O persists for the lifetime of the reference.

2. Change in 11.9.3 [class.base.init] paragraph 8 as follows:

   > A temporary expression bound to Binding a reference member to an object that is the result of a temporary expression in a mem-initializer is ill-formed.

3. Change in 11.9.3 [class.base.init] paragraph 11 as follows:

   > A temporary expression bound to Binding a reference member to an object that is the result of a temporary expression from a default member initializer is ill-formed.

CWG 2024-03-21

The phrasing "If a reference is bound to an object O that is the result of a temporary expression, ..." can be interpreted to refer to any temporary expression, even one entirely unrelated to the initializer of the reference.

#### 1986. odr-use and delayed initialization

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

The current wording of 6.10.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.8.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 A { A(); };
      thread_local A a;

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

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

There is implementation divergence in the handling of this example.

Notes from the November, 2014 meeting:

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

Additional note, May, 2015:

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

Notes from the October, 2015 meeting:

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

#### 2148. Thread storage duration and order of initialization

Section: 6.10.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.10.3.2 [basic.start.static] 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.10.3.3 [basic.start.dynamic] Status: drafting Submitter: Davis Herring Date: 2019-11-06

According to 6.10.3.3 [basic.start.dynamic] 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.5 [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.

#### 2086. Reference odr-use vs implicit capture

Section: 7.5.6.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.6 [expr.prim.lambda] bullet 12.2 leads to cases where references that can never be odr-used are implicitly captured:

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

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

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

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

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

#### 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 [expr.static.cast] paragraph 4, 7.6.16 [expr.cond] paragraph 4, 7.6.10 [expr.eq] 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 issue 1781.

#### 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 [expr.new] 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.8.6.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 [expr.new] 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.8.6.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 *a, *b;
      a = malloc(0x21000000 * sizeof(int));
      b = a + 0x21000000;
      printf("%lx\n", (long)(b - a));

This will typically print e1000000 instead of 21000000.

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

#### 2182. Pointer arithmetic in array-like containers

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

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

Notes from the February, 2016 meeting:

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

#### 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 issue 2018.

#### 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 [expr.cond] 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 issue 2321, which added the second sub-bullet (the citation above reflects the wording after adoption of issue 2321), giving the result the cv-qualification of `T1` instead of that of `T2`. The suggested resolution would revert that accepted resolution.

#### 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:

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

CWG 2023-06-15

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

#### 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 [expr.const] bullet 5.4, an expression is a constant expression unless (among other reasons) it would evaluate

- an invocation of an undefined `constexpr` function

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.

#### 2656. Converting consteval lambda to function pointer in non-immediate context

Section: 7.7 [expr.const] Status: drafting 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:

      auto x = &*[]() consteval { return 42; };

Suggested resolution:

Make the conversion function of a lambda whose call operator is an immediate function also an immediate function.

#### 1680. Including `<initializer_list>` for range-based `for`

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

A simple example like

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

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

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

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

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

Notes from the September, 2013 meeting:

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

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

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

#### 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

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

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

#### 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 [dcl.fct] paragraph 5,

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

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

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

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

See also issue 1444.

#### 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 [dcl.fct] paragraph 3 specifies the grammar for parameter-declaration:

      parameter-declaration:
          attribute-specifier-seqopt thisopt decl-specifier-seq declarator
          attribute-specifier-seqopt thisopt decl-specifier-seq declarator = initializer-clause
          attribute-specifier-seqopt thisopt decl-specifier-seq abstract-declaratoropt
          attribute-specifier-seqopt thisopt decl-specifier-seq abstract-declaratoropt = initializer-clause

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 [dcl.stc] 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 [dcl.stc] 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 [dcl.typedef] 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.6 [dcl.fct.def]).

4. Change in 9.2.8 [dcl.inline] 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 [dcl.fct] paragraph 3 as follows:

         parameter-declaration:
             attribute-specifier-seqopt thisopt decl-specifier-seq defining-type-specifier-seq declarator
             attribute-specifier-seqopt thisopt decl-specifier-seq defining-type-specifier-seq declarator = initializer-clause
             attribute-specifier-seqopt thisopt decl-specifier-seq defining-type-specifier-seq abstract-declaratoropt
             attribute-specifier-seqopt thisopt decl-specifier-seq defining-type-specifier-seq abstract-declaratoropt = initializer-clause

CWG 2023-02-07

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

#### 1580. Default arguments in explicit instantiations

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

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

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

Notes from the April, 2013 meeting:

The intent is to prohibit default arguments in explicit instantiations.

#### 2327. Copy elision for direct-initialization with a conversion function

Section: 9.5 [dcl.init] Status: drafting Submitter: Richard Smith Date: 2016-09-30

Consider an example like:

      struct Cat {};
      struct Dog { operator Cat(); };

      Dog d;
      Cat c(d);

This goes to 9.5 [dcl.init] bullet 17.6.2:

> Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one is chosen through overload resolution (12.2 [over.match]). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

Overload resolution selects the move constructor of `Cat`. Initializing the `Cat&&` parameter of the constructor results in a temporary, per 9.5.4 [dcl.init.ref] bullet 5.2.1.2. This precludes the possitiblity of copy elision for this case.

This seems to be an oversight in the wording change for guaranteed copy elision. We should presumably be simultaneously considering both constructors and conversion functions in this case, as we would for copy-initialization, but we'll need to make sure that doesn't introduce any novel problems or ambiguities.

See also issue 2311.

This issue is addressed by paper P2828.

#### 1304. Omitted array bound with string initialization

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

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

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

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

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

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

Section: 9.5.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

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

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

On a related note, assuming the binding is permitted, the intent of the overload tiebreaker found in 12.2.4.3 [over.ics.rank] 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.5.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:

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

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

Notes from the February, 2012 meeting:

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

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

Additional note (October, 2012):

Removing “to an rvalue,” as suggested, would have the effect of negating the preference for binding a function lvalue to an lvalue reference instead of an rvalue reference because the case would now fall under the preceding bullet of 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.5.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.5.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.5.4 [dcl.init.ref] Status: drafting Submitter: Hubert Tong Date: 2014-01-07

In the following case,

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

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

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

Is this intended? There is implementation variation.

Notes from the February, 2014 meeting:

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

#### 1996. Reference list-initialization ignores conversion functions

Section: 9.5.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 S { operator struct D &(); } s;
      D &d{s};

#### 2742. Guaranteed copy elision for brace-initialization from prvalue

Section: 9.5.5 [dcl.init.list] Status: drafting Submitter: Jim X Date: 2023-06-06

Consider:

      struct A {
        A();
        A(A const&) = delete;
      };
      int main(){
        A a = A(A(A())); // #1
        A b = A{A{A{}}}; // #2
      }

#1 is well-formed per 9.5.1 [dcl.init.general] bullet 16.6.1. However, even though #2 is intended to have a similar effect, the relevant rule excludes non-aggregates, making the example ill-formed (because the relevant constructor is deleted); see 9.5.5 [dcl.init.list] bullet 3.2:

> If T is an aggregate class and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

There is implementation divergence: gcc and clang accept, MSVC rejects.

See also issue 2311.

Suggested resolution:

Change in 9.5.5 [dcl.init.list] bullet 3.2 as follows:

> If T is an aggregate class a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

CWG 2024-03-01

Recent MSVC no longer rejects the example. The suggested resolution would revert issue 2137, which is not desirable. The consensus is to try an initializer-list constructor first and then fall back to copy-initialization (with guaranteed copy elision). A change to 12.2.4.2.6 [over.ics.list] may be necessary.

#### 1854. Disallowing use of implicitly-deleted functions

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

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

Notes from the June, 2014 meeting:

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

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

Section: 9.8.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.8.1 [dcl.enum] paragraph 10,

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

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

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

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

      template<class T> struct A {
        enum E : T;
      };
      A<int> a;
      template<class T> enum A<T>::E : T { e1, e2 };
      A<int>::E e = A<int>::e1;

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

Notes from the October, 2012 meeting:

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

Additional notes (April, 2025)

Consider:

      struct S {
        enum E : int;
      };

      enum S::E : int { e1 };

      void f() {
        S::E::e1; // #1, all implementations accept
        S::e1;    // #2, all implementations accept
        e1;       // #3, only Clang accepts
      }

Reflector discussion seems to favor making #2 (because adding a "member" after the end of the class-specifier; see 11.4.1 [class.mem.general] paragraph 3) and #3 ill-formed.

#### 2131. Ambiguity with opaque-enum-declaration

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

The declaration

      enum E;

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

(See also issue 2363.)

#### 2505. Nested unnamed namespace of inline unnamed namespace

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

According to 9.9.2.2 [namespace.unnamed] paragraph 1,

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

> `inline`opt `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.,

        inline namespace {
          namespace { }
        }

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.9.2.2 [namespace.unnamed] 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.10 [namespace.udecl] Status: drafting Submitter: Christof Meerwald Date: 2022-03-23

Consider:

      template<int I>
      struct C { };

      struct B
      {
        C<1> foo();
        C<1> bar();
      };

      struct D : B
      {
        using B::foo;
        C<2> foo(this B &);

        using B::bar;
        C<2> bar(this D &);
      };

      struct DD : D
      {
        using D::foo;
        using D::bar;
      };

      void bar(D d, DD dd)
      {
        d.foo();
        dd.foo();

        d.bar();
        dd.bar();
      }

Which functions are called?

Subclause 9.10 [namespace.udecl] 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 {
        void f();    // #1
      };
      struct D : B {
        void f();
        using B::f;  // should not name #1
      };

Suggested resolution:

Change in 9.10 [namespace.udecl] 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 f(int);
>         virtual void f(char);
>         void g(int);
>         void h(int);
>         void i();
>         void j();
>       };

>       struct D : B {
>         using B::f;
>         void f(int);   // OK, D::f(int) overrides B::f(int)

>         using B::g;
>         void g(char);  // OK

>         using B::h;
>         void h(int);   // OK, D::h(int) hides B::h(int)

>         using B::i;
>         void i(this B &);  // OK

>         using B::j;
>         void j(this D &);  // OK, D::j() hides B::j()
>       };

>       void k(D* p)
>       {
>         p->f(1);        // calls D::f(int)
>         p->f('a');      // calls B::f(char)
>         p->g(1);        // calls B::g(int)
>         p->g('a');      // calls D::g(char)
>         p->i();         // calls B::i, because B::i as a member of D is a better match than D::i
>         p->j();         // calls D::j
>       }
>       ...

#### 1706. `alignas` pack expansion syntax

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

The grammar for alignment-specifier in 9.13.1 [dcl.attr.grammar] 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

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

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

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

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

        alignas(int) alignas(double)

This is counterintuitive and should be reexamined.

See also messages 24016 through 24021.

Notes from the February, 2014 meeting:

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

#### 2223. Multiple `alignas` specifiers

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

According to 9.13.2 [dcl.align] 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 alignas(16) A;
      struct alignas(32) A;

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:

      // module interface unit
      export module M;
      export enum E : int;
      enum E : int { e };

      // other translation unit
      import M;
      auto a = E::e;  // #1: OK?
      auto b = e;     // #2: OK?

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

See also issues 2588 (friend declarations) and 2480.

CWG 2022-11-10

See 10.2 [module.interface] 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 A {
        struct B {
          auto foo() { return 0; }
        };
        decltype(B().foo()) x;
      };

There does not appear to be a prohibition of cases like this, where the type of a member depends on the definition of a member function.

(See also issues 1360, 1397, and 2335.)

Additional notes (January, 2023):

The following example might be related:

      #include <type_traits>

      struct Bar {
        struct Baz {
          int a = 0;
        };
        static_assert(std::is_default_constructible_v<Baz>);
      };

Additional notes (November, 2023):

The following example is also rejected by implementations, but it is allowed by the rules:

        struct A {
          static bool f2() { return f(); }

          static consteval bool f() { return true; }
        };

#### 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 [class.ctor] 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 [dcl.type.cv] paragraph 2 that would also need to be updated if this restriction is changed.)

It would also be helpful to add a requirement to 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 1460, 1562, 1587, and 1621.

#### 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 A {
        template<typename ...T> A(T...); // #1
        A(std::initializer_list<long>);  // #2
      };
      A a{};

According to 9.5.5 [dcl.init.list] paragraph 3, `A` will be value-initialized if it has a default constructor, and there is implementation divergence whether this example calls #1 or #2.

Similarly, for an example like

      struct B {
        template<typename T=int> B(T = 0);
      };

it is not completely clear whether a default constructor should be implicitly declared or not.

More generally, do utterances in the Standard concerning “constructors” also apply to constructor templates?

Notes from the February, 2014 meeting:

One possibility discussed was that we may need to change places that explicitly refer to a default constructor to use overload resolution, similar to the change that was made a few years ago with regard to copy construction vs “copy constructor.” One additional use of “default constructor” is in determining the triviality of a class, but it might be a good idea to remove the concept of a trivial class altogether. This possibility will be explored.

Notes from the February, 2016 meeting:

CWG reaffirmed the direction from the preceding note and also determined that the presence of a constructor template should suppress implicit declaration of a default constructor.

Additional notes (April, 2024)

The standard does not seem to contain a use of "trivial class" outside of examples. See paper P3247R0 Deprecate the notion of trivial types.

Additional examples to consider for the definition of "trivial class":

      struct A {
        A() = default; //eligible, second constructor unsatisfied
        template<class... Args>
        A(Args&&... args) requires (sizeof...(Args) > 0) {}
      };

      struct B {
        B() = default; //ineligible, second constructor more constrained
        template<class... Args>
        B(Args&&... args) requires (sizeof...(Args) == 0) {}
      };

      struct C {
        C() = default; //eligible, but
        template<class... Args> //also eligible and non-trivial
        C(Args&&... args) {}
      };

See also issue 1363.

#### 2799. Inheriting default constructors

Section: 11.4.5.2 [class.default.ctor] Status: drafting Submitter: Hubert Tong Date: 2017-09-01

(See also submission #545.)

Consider:

      struct A { int n; };
      struct B : A {
        using A::A;
        B(int);
      };

Does `B` have a default constructor?

Suggested resolution [SUPERSEDED]:

1. Change in 9.10 [namespace.udecl] paragraph 4 as follows:

   > If a constructor or assignment operator brought from a base class into a derived class has the signature of a default constructor or copy/move constructor or assignment operator for the derived class (11.4.5.2 [class.default.ctor], 11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]), the using-declaration does not by itself suppress the implicit declaration of the derived class member; the member from the base class is hidden or overridden by the implicitly-declared copy/move constructor or assignment operator special member function of the derived class, as described below.

2. Change in 11.4.5.2 [class.default.ctor] paragraph 1 as follows:

   > A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no user-declared constructor for class `X`, or if `X` inherits (9.10 [namespace.udecl]) one or more default constructors and there is no user-declared default constructor for `X`, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.6 [dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class.

Proposed resolution [SUPERSEDED]:

(This also resolves issue 2632.)

1. Change in 9.10 [namespace.udecl] paragraph 4 as follows:

   > If a constructor or assignment operator brought from a base class into a derived class has the signature of a copy/move constructor or assignment operator for the derived class (11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]), the using-declaration does not by itself suppress the implicit declaration of the derived class member; the member from the base class is hidden or overridden by the implicitly-declared copy/move constructor or assignment operator of the derived class, as described below. [ Note: A using-declarator that names a member function of a base class does not suppress the implicit declaration of a special member function in the derived class, even if their signatures are the same (11.4.5.2 [class.default.ctor], 11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]). -- end note ]

2. Add a new paragraph before 11.4.1 [class.mem.general] paragraph 2 as follows:

   > ... For any other member-declaration, each declared entity that is not an unnamed bit-field (11.4.10 [class.bit]) is a member of the class, termed a user-declared member, and each such member-declaration shall either declare at least one member name of the class or declare at least one unnamed bit-field.

3. Change in 11.4.5.2 [class.default.ctor] paragraph 1 as follows:

   > A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no a class `X` does not have a user-declared constructor for class `X`, or if `X` inherits (9.10 [namespace.udecl]) one or more default constructors and `X` does not have a user-declared default constructor, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.6 [dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class. [ Example:

   >       struct A {};
   >       struct B {};
   >       struct C : A, B {
   >         using A::A, B::B;
   >         C(int);
   >       };
   >       C c;     // OK

   >       struct X { X(int = 0, int = 0); };  // #1
   >       struct Y { Y(int = 0); };           // #2
   >       struct Z : X, Y {
   >         using X::X, Y::Y;
   >       };
   >       Z z2(1, 1);   // OK, invokes X(1, 1) and Y()
   >       Z z1(1);      // error: ambiguous between #1 and #2
   >       Z z0;         // OK, invokes X() and Y()

   > -- end example ]

4. Change in 11.4.5.3 [class.copy.ctor] paragraph 6 as follows:

   > If the class definition does not explicitly declare have a user-declared copy constructor, a non-explicit one is declared implicitly. ...

5. Change in 11.4.5.3 [class.copy.ctor] paragraph 8 as follows:

   > If the definition of a class X does not explicitly declare have a user-declared move constructor, a non-explicit one will be implicitly declared as defaulted if and only if ...

6. Add a new paragraph before 11.4.5.3 [class.copy.ctor] paragraph 11 as follows:

   > [ Note: A using-declaration in a derived class C that names a constructor from a base class never suppresses the implicit declaration of a copy/move constructor of C, even if the base class constructor would be a copy or move constructor if declared as a member of C. -- end note]

   > A copy/move constructor for class X is trivial if it is not user-provided and if: ...

7. Change in 11.4.6 [class.copy.assign] paragraph 2 as follows:

   > If the class definition does not explicitly declare have a user-declared copy assignment operator, one is declared implicitly. If the class definition declares has a user-declared move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defaulted (9.6 [dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor (D.6 [depr.impldec]). ...

Additional notes (November, 2023)

How does access checking interact with the proposed resolution above?

      struct B {
      protected:
        B(int = 0);
      };
      struct A : B {
        using B::B;
        A(void *);
      };
      A a;       // okay?
      A aa(42);  // not okay

CWG 2023-11-06

CWG resolved not to declare a default constructor in the derived class, but instead apply the usual rules for inherited constructors for this case. The wording should be changed so that the presence of a default constructor is never checked, in particular for "trivial class" (11.2 [class.prop], fixed by P3247R1 (Deprecate the notion of trivial types)), vacuous initialization (6.8.4 [basic.life] paragraph 1, fixed by issue 2859), and value initialization (9.5.1 [dcl.init.general] paragraph 9, also fixed by issue 2859).

#### 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:

        template<typename T> struct A {
           T t;
        };

        template <typename T> struct B {
           typename T::U u;
        };

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

        template <typename T> struct D {
           C<B<T> > v;
        };

        struct E {
           typedef A<D<E> > U;
        };

        extern A<D<E> > a;
        A<D<E> > a2(a);

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

Even if we wait until `A<D<E>>` is considered complete before finalizing the copy constructor declaration, declaring the copy constructor for `B<E>` will want to look at the copy constructor for `A<D<E>>`, so we still have the cycle.

I think that to avoid this cycle we need to short-circuit consideration of `C(const T&)` somehow. But I don't see how we can do that without breaking

        struct F {
           F(F&);
        };

        struct G;
        struct G2 {
           G2(const G&);
        };

        struct G {
           G(G&&);
           G(const G2&);
        };

        struct H: F, G { };

        extern H h;
        H h2(h);

Here, since `G`'s move constructor suppresses the implicit copy constructor, the defaulted `H` copy constructor calls `G(const G2&)` instead. If the move constructor did not suppress the implicit copy constructor, I believe the implicit copy constructor would always be viable, and therefore a better match than a constructor taking a reference to another type.

So perhaps the answer is to reconsider that suppression and then disqualify any constructor taking (a reference to) a type other than the constructor's class from consideration when looking up a subobject constructor in an implicitly defined constructor. (Or assignment operator, presumably.)

Another possibility would be that when we're looking for a conversion from `C<B<E>>` to `B<E>` we could somehow avoid considering, or even declaring, the `B<E>` copy constructor. But that seems a bit dodgy.

Additional note (October, 2010):

An explicitly declared move constructor/op= should not suppress the implicitly declared copy constructor/op=; it should cause it to be deleted instead. This should prevent a member function taking a (reference to) an un-reference-related type from being chosen by overload resolution in a defaulted member function.

And we should clarify that member functions taking un-reference-related types are not even considered during overload resolution in a defaulted member function, to avoid requiring their parameter types to be complete.

#### 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 issue 1360.

Notes from the September, 2013 meeting:

The problem with this approach is that hard errors (not in the immediate context) can occur, affecting portability. There are some cases, such as a virtual assignment operator in the base class, where lazy evaluation cannot be done, so it cannot be mandated.

#### 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 A
      {
        A();
        A(A&);
        explicit A(int);
        operator int() const;
      };
      struct B
      {
        B(B&& other);
        A a;
      };
      B::B(B&& other) : a(static_cast<B&&>(other).a) {}
      // B::B(B&& other) = default; // ill-formed

      void f(B& b1)
      {
        B b2 = static_cast<B&&>(b1);
      }

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 C {};

      struct A : C
      {
        A();
        A(A&);
        A(C&&);
      };
      struct B
      {
        B(B&& other);
        A a;
      };

      B::B(B&& other) = default;

#### 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 A { int x, y; };
      A passthrough(A a) { return a; }
      int main(void) {
       A a;
       a.x = 0;
       return passthrough(a).x;
      }

The default memberwise copying operation is not specified to be done in a way that is insensitive to indeterminate values.

#### 2158. Polymorphic behavior during destruction

Section: 11.4.7 [class.dtor] Status: drafting Submitter: Richard Smith Date: 2015-07-13

Consider the following example:

      #include <stdio.h>
      struct Base {
        Base *p;
        virtual void f() { puts("base"); }
        ~Base() {
          p->f();
        }
      };
      struct Derived : Base {
        Derived() { p = this; }
        void f() { puts("derived"); }
        void g() {
          p->f();
          delete this;
        }
      };
      void h() {
        Derived *p = new Derived;
        p->g();
      }

Should this have defined behavior? On the one hand, the `Derived` object is in its period of destruction, so the behavior of the `p->f()` call in the `Base` destructor should be to call `Base::f()`. On the other hand, `p` is a pointer to a `Derived` object whose lifetime has ended, and the rules in 6.8.4 [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.8.4 [basic.life] paragraph 7 appears to be to allow an implementation to assume that if it will perform two loads of a constant field (for instance, a const member, the implicit pointer for a reference member, or a vptr), and the two loads are performed on the “same pointer value”, then they load the same value.

Should there be a rule for destructors similar to that of 11.4.5 [class.ctor] paragraph 12?

> During the construction of a const object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor's this pointer, the value of the object or subobject thus obtained is unspecified.

#### 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:

      template <class ...> struct partition_indices {
        static auto compute_right () {}
        static constexpr auto right = compute_right;
      };
      auto foo () -> partition_indices<>;
      void f() {
        foo();
      };

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

      template <int> struct X {};
      template <class T> struct partition_indices {
        static auto compute_right () { return X<I>(); }
        static constexpr auto right = compute_right;
        static constexpr int I = sizeof(T);
      };

      auto foo () -> partition_indices<int>;

      void f() {
        foo();
      };

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:

      template <class T> struct A {
        static constexpr int num() { return 42; }
        int ar[num()];
      };
      A<int> a;

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 issues 1626 and 1890.

#### 1702. Rephrasing the definition of “anonymous union”

Section: 11.5 [class.union] Status: drafting Submitter: Richard Smith Date: 2013-06-17

11.5 [class.union] 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

        static union { int i; float f; };

is a declaration of that form (cf paragraph 6, which requires the `static` keyword for anonymous unions declared in namespace scope). However, it would be clearer if the definition were recast in more descriptive terms, e.g.,

> An anonymous union is an unnamed class that is defined with the class-key `union` in a simple-declaration in which the init-declarator-list is omitted. Such a simple-declaration is treated as if it contained a single declarator declaring an unnamed variable of the union's type.

(Note that this definition would require some additional tweaking to apply to class member anonymous union declarations, since simple-declarations are not included as member-declarations.)

As a related point, it is not clear how the following examples are to be treated, and there is implementation variance on some:

       void f() { thread_local union { int a; }; }
       void g() { extern union { int b; }; }
       thread_local union { int c; }; // static is implied by thread_local
       static thread_local union { int d; };
       static const union { int e = 0; }; // is e const? Clang says yes, gcc says no
       static constexpr union { int f = 0; };

Additional notes (July, 2023)

This issue is addressed by issue 2767.

#### 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 issue 7:

      class Foo { };
      class A : virtual private Foo { };
      class Bar : public A { };

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.

#### 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 [class.protected] paragraph 1 is no longer allowed following the change from issue 1873:

      class B {
      protected:
        int i;
        static int j;
      };
      // ...
      class D2 : public B {
        friend void fr(B*, D1*, D2*);
        void mem(B*, D1*);
      };
      void fr(B* pb, D1* p1, D2* p2) {
        // ...
        p2->B::i = 4;  // OK (access through a D2, even though naming class is B)
        // ...
      }

The example line ought to work, but none of the bullets in 11.8.3 [class.access.base] 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 [class.access.base] 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.

#### 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 Noncopyable {
        Noncopyable();
        Noncopyable(const Noncopyable &) = delete;
      };

      Noncopyable make(int kind = 0);

      struct AsBase : Noncopyable {
        AsBase() : Noncopyable(make()) {} // #1
      };

      struct AsMember {
        Noncopyable nc;
        AsMember() : nc(make()) { }  // #2?
      };

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 Noncopyable {
      Noncopyable();
      Noncopyable(const Noncopyable &) = delete;
      Noncopyable(int) : Noncopyable(Noncopyable()) {} // #3?
    };

#### 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 [over.call.func] paragraph 3 reads,

> An implied object argument must be contrived to correspond to the implicit object parameter attributed to member functions during overload resolution. It is not used in the call to the selected function. Since the member functions all have the same implicit object parameter, the contrived object will not be the cause to select or reject a function.

It is not true that “the member functions all have the same implicit object parameter.” This statement does not take into account member functions brought into the class by using-declarations or cv-qualifiers and ref-qualifiers on the non-static member functions:

        struct B
        {
          char f();         // B &
        };

        struct D : B
        {
          using B::f;
          long f();         // D &

          char g() const;   // D const &
          long g();         // D &

          char h() &;       // D &
          long h() &&;      // D &&
        };

        int main()
        {
          // D::f() has better match than B::f()
          decltype(D().f()) *p1 = (long *)0;

          // D::g() has better match than D::g() const
          decltype(D().g()) *p2 = (long *)0;

          // D::h() & is not viable function
          // D::h() && is viable function
          decltype(D().h()) *p3 = (long *)0;
        }

The value category of a contrived object expression is not specified by the rules and, probably, cannot be properly specified in presence of ref-qualifiers, so the statement “the contrived object will not be the cause to select or reject a function” should be normative rather than informative:

        struct X
        {
          static void f(double) {}
          void f(int) & {}
          void f(int) && {}
        };

        int main()
        {
          X::f(0); // ???
        }

#### 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 [over.call.object] paragraph 2 considers only those conversion functions matching a particular grammar pattern. This unintendedly excludes conversion functions with an explicit object parameter (and, as a pre-existing defect, `noexcept` conversion functions):

> In addition, for each non-explicit conversion function declared in T of the form

>     operator conversion-type-id ( ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt ;

> 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

>     R call-function ( conversion-type-id F, P1 a1 , ... , Pn an ) { return F (a1 , . . . , an ); }

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

      using fn_t = void();
      struct C {
        operator fn_t * (this C const &);
      };

      void foo(C c) {
        c();
      }

#### 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 issue 1687. As a result, `t+u` is ill-formed because it is resolved to the built-in `operator+(int*,std::ptrdiff_t)`, although most implementations do not (yet) agree:

      struct Adaptor { Adaptor(int); };

      struct List { };
      void operator +(List &, Adaptor);

      struct DataType {
        operator int *() const = delete;
        operator List &() const;
      };

      struct Yea;
      struct Nay { int theNaysHaveIt; };

      template <typename T, typename U>
      Yea addCheck(int, T &&t, U &&u, char (*)[sizeof(t + u, 0)] = 0);

      template <typename T, typename U>
      Nay addCheck(void *, T &&t, U &&u);

      void test(DataType &data) { (void)sizeof(addCheck(0, data,
      0.).theNaysHaveIt); }

It might be better to adjust the candidate list in 12.2.2.4 [over.match.ctor] bullet 3.3.3 to allow conversion only on class types and exclude the second standard conversion sequence.

#### 2028. Converting constructors in rvalue reference initialization

Section: 12.2.2.7 [over.match.ref] Status: drafting Submitter: Mitsuru Kariya Date: 2014-10-25

Consider the following example:

      struct T {
        T() {}
        T(struct S&) {}
      };

      struct S {
        operator T() { return T(); }
      };

      int main()
      {
        S s;
        T&& t(s);  // #1
      }

Because there are two possible conversions from `S` to `T`, one by conversion function and the other by converting constructor, one might expect that the initialization at #1 would be ambiguous. However, 12.2.2.7 [over.match.ref] (used in the relevant bullet of 9.5.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.5.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.5.4 [dcl.init.ref] says. See also issue 2077.

#### 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 [over.match.list] paragraph 1 says,

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

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

See also issue 1518.

Proposed resolution (January, 2017):

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

> When objects of non-aggregate class type `T` are list-initialized such that 9.5.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.5.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 A { A(); A(initializer_list<int>); };
      void f(A a);
      int main() { f({}); }

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

      template<typename T = int> using X = vector<int>;
      X x = {1, 2, 3};

      template<typename...> using Y = vector<int>;
      Y y = {1, 2, 3};

CTAD deduces `vector<int>`. Then we are asked to perform a check that the arguments of `X` and `Y` are deducible from `vector<int>`.

I think this check should succeed, deducing `T = int` in the first case and `<`pack`> = <`empty`>` 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

      template<typename T> struct AA;
      template<typename T = int> struct AA<X<T>>;

and

      template<typename T> struct AA;
      template<typename ...Ts> struct AA<Y<Ts...>>;

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

      template<typename T> struct AA {};
      template<typename T = int> void f(AA<X<T>>);

and

      template<typename T> struct AA {};
      template<typename ...Ts> void f(AA<Y<Ts...>>);

respectively, when given an argument of type `AA<`deduced return type`>`. 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:

      template<class T> struct S {
        template<class U> struct N {
          N(T) {}
          N(T, U) {}
          template<class V> N(V, U) {}
        };
      };
      S<int>::N x{2.0, 1};

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<int>::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 A { A(); } a;
      A a1 = {a}, a2 = {{a}}, a3 = {{{a}}};

`a1` and `a2` are valid, `a3` is ill-formed, because 12.2.4.2 [over.best.ics] bullet 4.5 allows one pair of braces and 12.2.4.2.6 [over.ics.list] 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 1467 and 2076, `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.

#### 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 issue 1604 broke the following example:

      struct A {};
      struct B { operator const A() const; };
      void f(A const&);
      void f(A&&);

      int main() {
        B a;
        f(a);
      }

Overload resolution selects the `A&&` overload, but then initialization fails. This seems like a major regression; we're now required to reject

       std::vector<A> va;
       B b;
       va.push_back(b);

Should we update 12.2.4.2.5 [over.ics.ref] to match the changes made to 9.5.4 [dcl.init.ref]?

See also issue 2108.

#### 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 [over.ics.list] 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.5.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.5.4 [dcl.init.ref], which also does not handle initializer lists, and not to 9.5.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 [over.ics.list] paragraph 5 needs to be clearer on how the expression specification is intended to be applied to initializer lists.

#### 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 A { A(); };
      struct B : A {};
      struct C : B {};

      void f1(B&);
      void f1(A);

      void f2(B);
      void f2(A&);

      int main()
      {
         C v;
         f1(v); // all compilers choose f1(B&)
         f2(v); // all compilers choose f2(B)
      }

The Standard does not appear to specify what happens in this case.

#### 1989. Insufficient restrictions on parameters of postfix operators

Section: 12.4 [over.oper] Status: drafting Submitter: Richard Smith Date: 2014-08-30

According to 12.4.7 [over.inc] paragraph 1,

> The user-defined function called `operator++` implements the prefix and postfix `++` operator. If this function is a non-static member function with no parameters, or a non-member function with one parameter, it defines the prefix increment operator `++` for objects of that type. If the function is a non-static member function with one parameter (which shall be of type `int`) or a non-member function with two parameters (the second of which shall be of type `int`), it defines the postfix increment operator `++` for objects of that type.

According to 12.4 [over.oper] paragraph 8,

> Operator functions cannot have more or fewer parameters than the number required for the corresponding operator, as described in the rest of this subclause.

This does not rule out an `operator++` with more than two parameters, however, since there is no corresponding operator.

One possibility might be to add a sentence like,

> A function named `operator++` shall declare either a prefix or postfix increment operator.

#### 205. Templates and static data members

Section: 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 [temp] paragraph 1 says that templates are only "classes or functions" but implies that "a static data member of a class template or of a class nested within a class template" is defined using the template-declaration syntax.

There are many places in the clause, however, where static data members of one sort or another are overlooked. For instance, Clause 13 [temp] paragraph 6 allows static data members of class templates to be declared with the `export` keyword. I would expect that static data members of (non-template) classes nested within class templates could also be exported, but they are not mentioned here.

Paragraph 8, however, overlooks static data members altogether and deals only with "templates" in defining the effect of the `export` keyword; there is no description of the semantics of defining a static data member of a template to be exported.

These are just two instances of a systematic problem. The entire clause needs to be examined to determine which statements about "templates" apply to static data members, and which statements about "static data members of class templates" also apply to static data members of non-template classes nested within class templates.

(The question also applies to member functions of template classes; see issue 217, where the phrase "non-template function" in 9.3.4.7 [dcl.fct.default] paragraph 4 is apparently intended not to include non-template member functions of template classes. See also issue 108, which would benefit from understanding nested classes of class templates as templates. Also, see issue 249, in which the usage of the phrase "member function template" is questioned.)

Notes from the 4/02 meeting:

Daveed Vandevoorde will propose appropriate terminology.

#### 1463. `extern "C"` alias templates

Section: 13.1 [temp.pre] Status: drafting Submitter: Daveed Vandevoorde Date: 2011-08-19 Liaison: EWG

Currently 13.1 [temp.pre] 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 issue 13).

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 [temp.param] paragraph 8 appears to be underspecified. For example, implementations vary in their treatment of

      template<typename T, T[T::size]> struct A {};
      int dummy;
      A<int, &dummy> a;

and

      template<typename T, T[1]> struct A;
      template<typename T, T*> struct A {};
      int dummy;
      A<int, &dummy> a;

See also issues 1322 and 1668.

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?

      #include <type_traits>

      template<class T>
      struct X {
        template<class U = typename T::type>
        static void foo(int){}
        static void foo(...){}
      };

      int main(){
        X<std::enable_if<false>>::foo(0);
      }

Also, is the effect on lookup the same? E.g.,

      struct S {
        template<typename T = U> void f();
        struct U {};
      };

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:

      template<class ...Types> struct Tuple_ { // _VARIADIC_TEMPLATE
        template<Types ...T, int I> int f() {
          return sizeof...(Types);
        }
      };
      int main() {
        Tuple_<char,int> a;
        int b = a.f<1, 2, 3>();
      }

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 issue 2383 for a related example.

#### 2903. Can we omit the `template` disambiguator in nested-name-specifiers in type-only contexts?

Section: 13.3 [temp.names] Status: drafting Submitter: Richard Smith Date: 2024-06-13

Are the following examples well-formed? Note the absence of a `template` disambiguator:

      template<typename T> class C {
       T::X<int> g1();      // #1
       T::X<int>::Y g2();   // #2
      };

      template<typename T>
      T::X<int> h1();       // #3
      template<typename T>
      T::X<int>::Y h2();    // #4

The return type is a type-only context per 13.8.1 [temp.res.general] paragraph 4. However, 13.3 [temp.names] paragraph 3 excludes nested-name-specifiers from the set of situations where `template` can be omitted. That means that #1 and #3 are valid; `X` is not part of a nested-name-specifier. However, #2 and #4 are invalid; the `template` disambiguator is missing. Those examples ought to be valid, too.

Proposed resolution (approved by CWG 2024-06-14):

Change in 13.3 [temp.names] paragraph 3 as follows:

> A name is in a transitive type-only context if
> - it is in a type-only context (13.8.1 [temp.res.general]) and is not the terminal name of a nested-name-specifier, or
> - it is the terminal name of a nested-name-specifier that precedes a name in a transitive type-only context (possibly with an intervening `template` keyword).
> A < is interpreted as the delimiter of a template-argument-list if it follows a name that is not a conversion-function-id and
> - that follows the keyword template or a ~ after a nested-name-specifier or in a class member access expression, or
> - for which name lookup finds the injected-class-name of a class template or finds any declaration of a template, or
> - that is an unqualified name for which name lookup either finds one or more functions or finds nothing, or
> - that is a terminal name in a using-declarator (9.10 [namespace.udecl]), in a declarator-id (9.3.4 [dcl.meaning]), or in a transitive type-only context other than a nested-name-specifier (13.8 [temp.res]).

#### 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 [temp.arg.nontype] paragraph 1 (newly revised by the adoption of paper N4268),

> For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

> - a subobject (6.8.2 [intro.object]),

> - ...

This change breaks an example like

       template<int *p> struct X {};
       int arr[32];
       X<arr> x;

because the array-to-pointer decay produces a pointer to the first element, which is a subobject.

Suggested resolution:

Change the referenced bullet to read:

- a subobject (6.8.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

        template<char &p> struct S { };
        char arr[2];
        S<arr[0]> s_arr;

which may not be exactly what we want.

See also issue 2401.

#### 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:

      template <const char *N> struct A { static const int val; };

      template <const char *N> const int A<N>::val = 0;

      static const char c[2] = "";

      int main() {
        A<c> a;
        return A<c>::val;
      }

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 issue 2043.

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

#### 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:

      template<typename T1, typename T2 = char> class A { };

      template<template<typename... T> class X> class S {
        X<int> x;
      };

      S<A> a;

Issue 184 dealt with a similar question but did so in the era before variadic templates. This usage should be permitted in modern C++.

Notes from the February, 2016 meeting:

CWG felt that this usage should be permitted, but only for template template parameters with a parameter pack.. Furthermore, if the template template parameter has a default argument followed by a parameter pack, the parameter's default argument would be used, followed by any remaining default arguments from the template template argument.

#### 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:

      template<class T, class U = T> class B { /* ... */ };
      template<template<class> class P, class T> void f(P<T>);

      int main()  {
        f(B<int>());       // OK?
        f(B<int,float>()); // ill-formed, T deduced to int and float
      }

In deduction we can determine that `P` is more specialized than `B`, then substitute `B` into `P<T>`, and then compare `B<T,T>` to `B<int,int>`. This will allow deduction to succeed, whereas comparing `<T>` to `<int,int>` 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;

      template<typename> struct match;

      template<template<typename> class t,typename T>
      struct match<t<T> > { typedef int type; };      // #1

      template<template<typename,typename> class t,typename T0,typename T1>
      struct match<t<T0,T1> > { typedef int type; };  // #2

      template<typename,typename = void> struct other { };
      typedef match<other<void,void> >::type type;

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

      template<typename T>
      void fn (match<other<T>>); // i.e. other<T,void>
      template<typename T0, typename T1>
      void fn (match<other<T0,T1>>);

So #1 is more specialized, whereas before this change we chose #2.

Additional notes (May, 2024)

Paper P3310R0 (Solving partial ordering issues introduced by P0522R0) by Matheus Izvekov strives to solve this issue, currently with focus on partial ordering (only).

#### 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,

      template<int N> struct A {};
      template<short N> using B = A<N>;
      template<int N> void f(B<N>) {} // #1
      template<int N> void f(A<N>) {} // #2

There is implementation variance as to whether there is one `f` or two. As with previously-discussed cases, these have different SFINAE effects, perhaps equivalent but not functionally equivalent. Should the argument to #1 be treated as something like `A<(int)(short)N>` and not just `A<N>`.

See also issues 1668 and 1979.

#### 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,

      template<char...> struct char_values {};
      template<int C1, char C3>
      struct char_values<C1, 12, C3> {
        static const unsigned value = 1;
      };
      int check0[char_values<1, 12, 3>::value == 1? 1 : -1];

The closest the current wording comes to dealing with this question is 13.7.6.1 [temp.spec.partial.general] 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 1315, 2033, and 2127.)

#### 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:

      template<class T, T*> struct X { };
      // We want to partially specialize for all nullptrs...
      template<class T> struct X<T, nullptr> { ... }; // NOT OK

This is disallowed by the rule in bullet 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 1315, 1647, and 2033.)

#### 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 [temp.spec.partial.general] paragraph 1,

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

There are two problems with this wording. First, the “no diagnostic required” provision is presumably to avoid mandating cross-translation-unit analysis, but there is no reason not to require the diagnostic if the rule is violated within a single translation unit. Also, “would make use” is imprecise; it could be interpreted as applying only when the partial specialization would have been selected by a previous specialization, but it should also apply to cases where the partial specialization would have made a previous specialization ambiguous.

Making these two changes would guarantee that a diagnostic is issued for the following example:

       template <class T1, class T2> class A;
       template <class T> struct A<T, void> { void f(); };
       template <class T> void g(T) { A<char, void>().f(); }   // #1
       template<typename T> struct A<char, T> {};
       A<char, void> f;   // #2

It is unspecified whether the reference to `A<char, void>` at #1 is the “first use” or not. If so, `A<char, void>` is bound to the first partial specialization and, under the current wording, an implementation is not required to diagnose the ambiguity resulting from the second partial specialization. If #2 is the “first use,” it is clearly ambiguous and must result in a diagnostic. There is implementation divergence on the handling of this example that would be addressed by the suggested changes.

#### 549. Non-deducible parameters in partial specializations

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

        template <class T> struct A { typedef T U; };
        template <class T> struct C { };
        template <class T> struct C<typename A<T>::U> { };

Several compilers issue errors for this case, but there appears to be nothing in the Standard that would make this ill-formed; it simply seems that the partial specialization will never be matched, so the primary template will be used for all specializations. Should it be ill-formed?

(See also issue 1246.)

Notes from the April, 2006 meeting:

It was noted that there are similar issues for constructors and conversion operators with non-deducible parameters, and that they should probably be dealt with similarly.

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 issue 1315 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 [temp.spec.partial.member] 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

      template<typename A> struct X { template<typename B> struct Y; };
      template struct X<int>;
      template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
      int k = X<int>::Y<int*>().n;

is the last line valid? There is implementation variance on this point. Similarly, for an example like

      template<typename A> struct Outer {
       template<typename B, typename C> struct Inner;
      };
      Outer<int> outer;
      template<typename A> template<typename B>
        struct Outer<A>::Inner<typename A::error, B> {};

at what point, if at all, is the declaration of the partial specialization instantiated? Again, there is implementation variance in the treatment of this example.

Notes from the February, 2014 meeting:

CWG decided that partial specialization declarations should be instantiated only when needed to determine whether the partial specialization matches or not.

Additional note, November, 2014:

See also paper N4090.

#### 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:

      template <class T> using foo = typename T::foo;

      class B {
        typedef int foo;
        friend struct C;
      };

      struct C {
        foo<B> f;    // Well-formed?
      };

Is the substitution of `B::foo` for `foo<B>` done in the context of the befriended class `C`, making the reference well-formed, or is the access determined independently of the context in which the alias template specialization appears?

If the answer to this question is that the access is determined independently from the context, care must be taken to ensure that an access failure is still considered to be “in the immediate context of the function type” (13.10.3 [temp.deduct] paragraph 8) so that it results in a deduction failure rather than a hard error.

Notes from the October, 2012 meeting:

The consensus of CWG was that instantiation (lookup and access) for alias templates should be as for other templates, in the definition context rather than in the context where they are used. They should still be expanded immediately, however.

Additional note (February, 2014):

A related problem is raised by the definition of `std::enable_if_t` (21.3.3 [meta.type.synop]):

      template <bool b, class T = void>
      using enable_if_t = typename enable_if<b,T>::type;

If `b` is `false`, there will be no `type` member. The intent is that such a substitution failure is to be considered as being “in the immediate context” where the alias template specialization is used, but the existing wording does not seem to accomplish that goal.

Additional note, November, 2014:

Concern has been expressed that the intent to analyze access in the context of the alias template definition is at odds with the fact that friendship cannot be granted to alias templates; if it could, the access violation in the original example could be avoided by making `foo` a friend of class `B`, but that is not possible.

Additional node, February, 2016:

The issue has been returned to "open" status to facilitate further discussion by CWG as to whether the direction in the October, 2012 note is still desirable.

Notes from the February, 2016 meeting:

CWG reaffirmed the direction described in the October, 2012 note above. With regard to the November, 2014 note regarding granting of friendship, it was observed that the same problem occurs with enumerators, which might refer to inaccessible names in the enumerator volue. The solution in both cases is to embed the declaration in a class and grant the class friendship. See issue 1844, dealing with the definition of “immediate context.”

CWG 2024-06-28

See issue 2296 for 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

      template<typename T> struct A {
        struct B {
          void f();
        };
      };

      template<typename T> using X = typename A<T>::B;

      template<typename T> void X<T>::f() { }       // #1

should #1 be considered a definition of `A<T>::B::f()`?

Analogy with alias-declarations would suggest that it should, but alias template specializations involve issues like SFINAE on unused template parameters (see issue 1558) and possibly other complications.

(See also issues 1980, 2021, 2025, 2037, and 2794.)

Notes from the May, 2015 meeting:

CWG felt that this kind of usage should be permitted only via a “simple” alias, in which the SFINAE is the same as the template to which it refers and all the template parameters are used. See also issue 1286.

#### 1980. Equivalent but not functionally-equivalent redeclarations

Section: 13.7.8 [temp.alias] Status: drafting Submitter: Richard Smith Date: 2014-08-04

In an example like

      template<typename T, typename U> using X = T;
      template<typename T> X<void, typename T::type> f();
      template<typename T> X<void, typename T::other> f();

it appears that the second declaration of `f` is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.

Notes from the November, 2014 meeting:

CWG felt that these two declarations should not be equivalent.

#### 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:

      struct A { typedef int type; };
      template <typename T> using ALIAS = A;

      template <typename T> void foo()
      {
         ALIAS<T>::type t; // Is typename required here?
      }

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

See also issues 1558, 1979, and 2037.

#### 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 [temp.dep.type] 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:

        struct B { typedef int type; };
        template<typename> using foo = B;
        template<typename T> void f() {
          foo<T>::type * x;  //error: typename required
        }

Is a change to the rules for cases like this warranted?

Notes from the October, 2012 meeting:

CWG agreed that no `typename` should be required in this case. In some ways, an alias template specialization is like the current instantiation and can be known at template definition time.

#### 1524. Incompletely-defined class template base

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

      template<typename T> struct A {
        struct B: A { };
      };

A type used as a base must be complete (11.7 [class.derived] paragraph 2) . The fact that the base class in this example is the current instantiation could be interpreted as indicating that it should be available for lookup, and thus the normal rule should apply, as members declared after the nested class would not be visible.

On the other hand, 13.8.3 [temp.dep] paragraph 3 says,

> In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

This wording refers not to a dependent type, which would permit lookup in the current instantiation, but simply to a type that “depends on a template-parameter,” and the current instantiation is such a type.

Implementations vary on the handling of this example.

(See also issue 1526 for another case related to the distinction between a “dependent type” and a “type that depends on a template-parameter.”)

Notes from the October, 2012 meeting:

CWG determined that the example should be ill-formed.

#### 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 [temp.dep.type] paragraph 9, a local class in a function template is dependent if and only if it contains a subobject of a dependent type. However, given an example like

      template<typename T> void f() {
        struct X {
          typedef int type;
      #ifdef DEPENDENT
          T x;
      #endif
        };
      X::type y;    // #1
      }
      void g() { f<int>(); }

there is implementation variance in the treatment of #1, but whether or not `DEPENDENT` is defined appears to make no difference.

In a related question, should a value-dependent `alignas` specifier cause a type to be dependent? Given

      template<int N> struct Y { typedef int type; };
      template<int N> void h() {
        struct alignas(N) X {};
        Y<alignof(X)>::type z;   // #2
      }
      void i() { h<4>(); }

Most/all implementations issue an error for a missing `typename` in #2.

Perhaps the right answer is that the types should be dependent but a member of the current instantiation, permitting name lookup without `typename`.

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.8.2 [enum.udecl] 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:

    template <class T>
    void f() {
      enum class E { e1, e2 };
      using enum E;
    }

#### 2275. Type-dependence of function template

Section: 13.8.3.3 [temp.dep.expr] Status: drafting Submitter: Jason Merrill Date: 2016-06-21

Consider:

      struct B { template <class T> void h(); };
      template <class T> struct A {
        template <class U> static U f(U);
        void g() {
         f(B()).h<int>(); // OK, f(B()) is non-type-dependent with type B.
        }
      };

A member template ought to be dependent only if it depends on template parameters of the current scope, but 13.8.3.3 [temp.dep.expr] 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:

      using T = int[];
      using U = int[2];
      template<auto M, int ...N> void f() {
        auto &&arr1 = T(N...);
        auto &&arr2 = T{N...};
        auto &&arr3 = U(M, M);
        auto &&arr4 = U{M, M};
      };

I think here `T(N...)` is not type-dependent, per 13.8.3.3 [temp.dep.expr] 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 [temp.dep.expr] 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.

#### 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

        template <class T> class Foo {

           public:
           typedef int Bar;
           Bar f();
        };
        template <class T> typename Foo<T>::Bar Foo<T>::f() { return 1;}
                           --------------------

In the class template definition, the declaration of the member function is interpreted as:

       int Foo<T>::f();

In the definition of the member function that appears outside of the class template, the return type is not known until the member function is instantiated. Must the return type of the member function be known when this out-of-line definition is seen (in which case the definition above is ill-formed)? Or is it OK to wait until the member function is instantiated to see if the type of the return type matches the return type in the class template definition (in which case the definition above is well-formed)?

Suggested resolution: (John Spicer)

My opinion (which I think matches several posted on the reflector recently) is that the out-of-class definition must match the declaration in the template. In your example they do match, so it is well formed.

I've added some additional cases that illustrate cases that I think either are allowed or should be allowed, and some cases that I don't think are allowed.

        template <class T> class A { typedef int X; };

        template <class T> class Foo {
         public:
           typedef int Bar;
           typedef typename A<T>::X X;
           Bar f();
           Bar g1();
           int g2();
           X h();
           X i();
           int j();
         };

         // Declarations that are okay
         template <class T> typename Foo<T>::Bar Foo<T>::f()
                                                         { return 1;}
         template <class T> typename Foo<T>::Bar Foo<T>::g1()
                                                         { return 1;}
         template <class T> int Foo<T>::g2() { return 1;}
         template <class T> typename Foo<T>::X Foo<T>::h() { return 1;}

         // Declarations that are not okay
         template <class T> int Foo<T>::i() { return 1;}
         template <class T> typename Foo<T>::X Foo<T>::j() { return 1;}

In general, if you can match the declarations up using only information from the template, then the declaration is valid.

Declarations like `Foo::i` and `Foo::j` are invalid because for a given instance of `A<T>`, `A<T>::X` may not actually be int if the class is specialized.

This is not a problem for `Foo::g1` and `Foo::g2` because for any instance of `Foo<T>` that is generated from the template you know that Bar will always be int. If an instance of `Foo` is specialized, the template member definitions are not used so it doesn't matter whether a specialization defines `Bar` as int or not.

#### 287. Order dependencies in template instantiation

Section: 13.8.4.1 [temp.point] Status: drafting Submitter: Martin Sebor Date: 17 May 2001

Implementations differ in their treatment of the following code:

        template <class T>
        struct A {
    	typename T::X x;
        };

        template <class T>
        struct B {
    	typedef T* X;
    	A<B> a;
        };

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

Some implementations accept it. At least one rejects it because the instantiation of `A<B<int> >` requires that `B<int>` be complete, and it is not at the point at which `A<B<int> >` is being instantiated.

Erwin Unruh:

In my view the programm is ill-formed. My reasoning:

- you need a complete type `B<int>` because you declare a variable in `main`
- `B<int>` contains a member of type `A<B<int> >`, so you need that complete.
- `A<B<int> >` tries to access `B<int>::X`, which in turn needs `B<int>` being complete.

So each class needs the other to be complete.

The problem can be seen much easier if you replace the typedef with

        typedef T (*X) [sizeof(B::a)];

Now you have a true recursion. The compiler cannot easily distinguish between a true recursion and a potential recursion.

John Spicer:

Using a class to form a qualified name does not require the class to be complete, it only requires that the named member already have been declared. In other words, this kind of usage is permitted:

        class A {
            typedef int B;
            A::B ab;
        };

In the same way, once `B` has been declared in `A`, it is also visible to any template that uses `A` through a template parameter.

The standard could be more clear in this regard, but there are two notes that make this point. Both 6.5.5.2 [class.qual] and _N4567_.5.1.1 [expr.prim.general] paragraph 7 contain a note that says "a class member can be referred to using a qualified-id at any point in its potential scope (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 [temp.point] paragraph 3, dealing with the point of instantiation:

> For a class template specialization... if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

That means that the point of instantiation of `A<B<int> >` is before that of `B<int>`, not in the middle of `B<int>` after the declaration of `B::X`, and consequently a reference to `B<int>::X` from `A<B<int> >` is ill-formed.

To put it another way, I believe John's approach requires that there be an instantiation stack, with the results of partially-instantiated templates on the stack being available to instantiations above them. I don't think the Standard mandates that approach; as far as I can see, simply determining the implicit instantiations that need to be done, rewriting the definitions at their respective points of instantiation with parameters substituted (with appropriate "forward declarations" to allow for non-instantiating references), and compiling the result normally should be an acceptable implementation technique as well. That is, the implicit instantiation of the example (using, e.g., `B_int` to represent the generated name of the `B<int>` specialization) could be something like

            struct B_int;

            struct A_B_int {
                B_int::X x;    // error, incomplete type
            };

            struct B_int {
                typedef int* X;
                A_B_int a;
            };

Notes from 10/01 meeting:

This was discussed at length. The consensus was that the template case should be treated the same as the non-template class case it terms of the order in which members get declared/defined and classes get completed.

Proposed resolution:

In 13.8.4.1 [temp.point] paragraph 3 change:

> the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

To:

> the point of instantiation is the same as the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the nearest enclosing declaration. [Note: The point of instantiation is still at namespace scope but any declarations preceding the point of instantiation, even if not at namespace scope, are considered to have been seen.]

Add following paragraph 3:

> If an implicitly instantiated class template specialization, class member specialization, or specialization of a class template references a class, class template specialization, class member specialization, or specialization of a class template containing a specialization reference that directly or indirectly caused the instantiation, the requirements of completeness and ordering of the class reference are applied in the context of the specialization reference.

and the following example

      template <class T> struct A {
              typename T::X x;
      };

      struct B {
              typedef int X;
              A<B> a;
      };

      template <class T> struct C {
              typedef T* X;
              A<C> a;
      };

      int main ()
      {
              C<int> c;
      }

Notes from the October 2002 meeting:

This needs work. Moved back to drafting status.

See also issues 595 and 1330.

#### 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:

      template<typename T> struct X;

      extern X<int> *p;
      void *q = +p; // #1, complete type affects semantics via ADL

      template<typename T> struct X {};
      X<int> x; // #2, ill-formed, X<int> is incomplete

According to the wording of issue 212, this program is ill-formed, because the single point of instantiation for `X<int>` is at #1, thus `X<int>` 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:

      template<typename T> constexpr T f();
      constexpr int g() { return f<int>(); } // #1
      template<typename T> constexpr T f() { return 123; }
      int k[g()];
      // #2

There are two points of instantiation for `f<int>`. 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 [temp.point] 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 (issue 2186) and constexpr functions are defined (issue 2166).

#### 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 [temp.inst] 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.6.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

      #include <type_traits>
      template<class T> struct Foo { Foo(T = nullptr) {} };
      bool b = std::is_constructible<Foo<int>>::value;
      int main() {}

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.

Additional notes (March, 2024)

This issue is related to issue 2296.

#### 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 [temp.inst] 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 `friend`s

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 [temp.decls.general] paragraph 3 or 13.9.2 [temp.inst] paragraph 2. Consider:

      template <typename T>
      struct A {
        friend void foo(A &&, int = T::happy) { }
      };

      int main(void) { foo(A<int>(), 0); }

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 issue 2174.

#### 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?

      template<typename ...T> struct Tuple;
      template<class T, class U> struct Outer;
      template<class ...T, class ...U>
      struct Outer<Tuple<T ...>, Tuple<U ...> > {
        template<class X, class Y> struct Inner;
        template<class ...Y> struct Inner<Tuple<T, Y> ...> { };
        template<class ...Y> struct Inner<Tuple<U, Y> ...> { };
      };
      Outer<Tuple<int, void>, Tuple<int, void> > outer;

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:

      struct Base {};

      template<int N>
      struct S : public Base {
        friend int foo(Base&) requires (N == 1) { return 1; }
        friend int foo(Base&) requires (N == 2) { return 3; }
      };

      int main() {
        S<1> s1{};
        S<2> s2{};  // #1
      }

The current wording does not seem to cover what happens for this case. In particular, 13.9.2 [temp.inst] paragraph 17 does not cover constrained non-template friends.

See also the Itanium ABI issue 24.

Suggested resolution:

1. Change in 13.7.5 [temp.friend] 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 [temp.inst] 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:

   >       struct Base {};

   >       template<int N>
   >       struct S : Base {
   >         friend int foo(Base&) requires (N == 1) { return 1; }  // #1
   >         friend int foo(Base&) requires (N == 2) { return 3; }  // #2
   >       };
   >       S<1> s1;
   >       S<2> s2;          // OK, no conflict between #1 and #2
   >       int x = foo(s1);  // OK, selects #1
   >       int y = foo(s2);  // OK, selects #2

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

      struct X {
        template<typename T> void f(T);
        void f(int);
      };
      template void X::f(int);

or

      template<typename T> void f(T) {}
      void f(int);
      template void f(int);

Presumably in both these cases the explicit instantiation should refer to the template and not to the non-template; however, 13.7.3 [temp.mem] paragraph 2 says,

> A normal (non-template) member function with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied.

This would appear to give the wrong answer for the first example. It's not clearly stated, but consistency would suggest a similar wrong answer for the second. Presumably a statement is needed somewhere that an explicit instantiation directive applies to a template and not a non-template function if both are visible.

Additional note, January, 2014:

A related example has been raised:

      template<typename T> class Matrix {
      public:
        Matrix(){}
        Matrix(const Matrix&){}
        template<typename U>
          Matrix(const Matrix<U>&);
      };

      template Matrix<int>::Matrix(const Matrix&);

      Matrix<int> m;
      Matrix<int> mm(m);

If the explicit instantiation directive applies to the constructor template, there is no way to explicitly instantiate the copy constructor.

#### 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 issue 2488 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 [temp.explicit] 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:

Issue 2488 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.

#### 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 issue 941 permits a non-deleted explicit specialization of a deleted function template. For example:

      template<typename T> void f() = delete;
      decltype(f<int>()) *p;
      template<> void f<int>();

However, the existing normative wording is not adequate to handle this usage. For one thing, `=delete` is formally, at least, a function definition, and an implementation is not permitted to instantiate a function definition unless it is used; presumably, then, an implementation could not reject the `decltype` above as a reference to a deleted specialization. Furthermore, there should be a requirement that a non-deleted explicit specialization of a deleted function template must precede any reference to that specialization. (I.e., the example should be ill-formed as written but well-formed if the last two lines were interchanged.)

#### 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:

      struct S {
        template <int N> static constexpr inline int m = N;
      };
      template <> constexpr inline int S::m<5>;

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:

      template <> constexpr inline int S::m<5> = 2;

This would seem to be disallowed both by 11.4.9.3 [class.static.data] 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 [temp.expl.spec] 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 [temp.arg.explicit] 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:

      template<typename... T> void f(typename T::type...)   {
      }

      int main() {
        f<>();
      }

See also issue 2105.

#### 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 issue 1273) was deemed relevant for this issue:

        template <class T> struct C;

        class A {
           int i;
           friend struct C<int>;
        } a;

        class B {
           int i;
           friend struct C<float>;
        } b;

        template <class T>
        struct C {
           template <class U> decltype (a.i) f() { } // #1
           template <class U> decltype (b.i) f() { } // #2
        };

        int main() {
           C<int>().f<int>();     // calls #1
           C<float>().f<float>(); // calls #2
        }

#### 1322. Function parameter type decay in templates

Section: 13.10.3 [temp.deduct] Status: drafting Submitter: Jason Merrill Date: 2011-05-19

The discussion of issue 1001 seemed to have settled on the approach of doing the 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 [temp.deduct] paragraph 8 suggests otherwise:

      template <class T> int f(T[5]);
      int I = f<int>(0);
      int j = f<void>(0); // invalid array

One way that might be addressed would be to separate the concepts of the type of the template that participates in overload resolution and function matching from the type of the template that is the source for template argument substitution. (See also the example in paragraph 3 of the same section.)

Notes, January, 2012:

#### 1582. Template default arguments and deduction failure

Section: 13.10.3 [temp.deduct] Status: drafting Submitter: John Spicer Date: 2012-10-31

According to 13.10.3 [temp.deduct] paragraph 5,

> The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. If a template argument has not been deduced and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default argument. If the substitution results in an invalid type, as described above, type deduction fails.

This leaves the impression that default arguments are used after deduction failure leaves an argument undeduced. For example,

      template<typename T> struct Wrapper;
      template<typename T = int> void f(Wrapper<T>*);
      void g() {
        f(0);
      }

Deduction fails for `T`, so presumably `int` is used. However, some implementations reject this code. It appears that the intent would be better expressed as something like

> ...If a template argument is used only in a non-deduced context and its corresponding template parameter has a default argument...

Rationale (November, 2013):

CWG felt that this issue should be considered by EWG in a broader context before being resolved.

Additional note, April, 2015:

EWG has requested that CWG resolve this issue along the lines discussed above.

Notes from the May, 2015 meeting:

CWG agreed that a default template argument should only be used if the parameter is not used in a deducible context. See also issue 2092.

#### 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 [temp.deduct.call] paragraph 1,

> If removing references and cv-qualifiers from `P` gives `std::initializer_list<P'>` for some `P'` and the argument is an initializer list (9.5.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking `P'` as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]).

It is not entirely clear whether the deduction for an initializer list meeting a `std::initializer_list<T>` is a recursive subcase, or part of the primary deduction. A relevant question is: if the deduction on that part fails, does the entire deduction fail, or is the parameter to be considered non-deduced?

See also issue 2326.

Notes from the October, 2012 meeting:

CWG determined that the entire deduction fails in this case.

#### 1584. Deducing function types from cv-qualified types

Section: 13.10.3.2 [temp.deduct.call] Status: drafting Submitter: Daniel Krügler Date: 2012-11-04

It is not clear whether the following is well-formed or not:

      void foo(){}

      template<class T>
      void deduce(const T*) { }

      int main() {
        deduce(foo);
      }

Implementations vary in their treatment of this example.

Proposed resolution (April, 2013):

Change 13.10.3.6 [temp.deduct.type] paragraph 18 as follows:

> A template-argument can be deduced from a function, pointer to function, or pointer to member function type. [Note: cv-qualification of a deduced function type is ignored; see 9.3.4.6 [dcl.fct]. —end note] [Example:

>       template<class T> void f(void(*)(T,int));
>       template<class T> void f2(const T*);
>       template<class T> void foo(T,int);
>       void g(int,int);
>       void g(char,int);
>       void g2();

>       void h(int,int,int);
>       void h(char,int);
>       int m() {
>         f(&g);     // error: ambiguous
>         f(&h);     // OK: void h(char,int) is a unique match
>         f(&foo);   // error: type deduction fails because foo is a template
>         f2(g2);    // OK: cv-qualification of deduced function type ignored
>       }

> —end example]

Additional note, November, 2014:

Concern was expressed regarding the proposed resolution over its treatment of an example like the following:

      template<typename T> struct tuple_size {};
      template<typename T> struct tuple_size<T const>: tuple_size<T> {};

      tuple_size<void()> t;

In this case `T const` is always considered to be more specialized for `void()`, leading to infinite self-derivation.

The issue has been returned to "open" status for further consideration.

Notes from the May, 2015 meeting:

The consensus of CWG was that the cv-qualification of the argument and parameter must match, so the original example should be rejected.

#### 1486. Base-derived conversion in member pointer deduction

Section: 13.10.3.3 [temp.deduct.funcaddr] Status: drafting Submitter: John Spicer Date: 2012-03-26

The rules for deducing template arguments when taking the address of a function template in 13.10.3.3 [temp.deduct.funcaddr] do not appear to allow for a base-to-derived conversion in a case like:

      struct Base {
        template<class U> void f(U);
      };

      struct Derived : Base { };

      int main() {
        void (Derived::*pmf)(int) = &Derived::f;
      }

Most implementations appear to allow this adjustment, however.

#### 1610. Cv-qualification in deduction of reference to array

Section: 13.10.3.5 [temp.deduct.partial] Status: drafting Submitter: Richard Smith Date: 2013-01-28

Given

       template<class C> void foo(const C* val) {}
       template<int N> void foo(const char (&t)[N]) {}

it is intuitive that the second template is more specialized than the first. However, the current rules make them unordered. In 13.10.3.5 [temp.deduct.partial] paragraph 4, we have P as `const C*` and A as `const char (&)[N]`. Paragraph 5 transforms A to `const char[N]`. Finally, paragraph 7 removes top-level cv-qualification; since a cv-qualified array element type is considered to be cv-qualification of the array (6.9.5 [basic.type.qualifier] paragraph 5, cf issue 1059), A becomes `char[N]`. P remains `const C*`, so deduction fails because of the missing `const` in A.

Notes from the April, 2013 meeting:

CWG agreed that the `const` should be preserved in the array type.

#### 2328. Unclear presentation style of template argument deduction rules

Section: 13.10.3.6 [temp.deduct.type] Status: drafting Submitter: Richard Smith Date: 2016-10-11

The presentation style of 13.10.3.6 [temp.deduct.type] paragraph 8 results in a specification that is unclear, needlessly verbose, and incomplete. Specific problems include:

- What does it mean for `P` and `A` to have one of a set of forms? Do they both have to have that form? (That doesn't happen; typically, only `P` contains template parameters)

- In the introductory sentence, aren't `T`, `TT`, and `i` supposed to be the names of template parameters rather than template arguments?

- In `T[i]`, it appears we can deduce `i`, but not `T` (`T` can only be deduced in the form `T[integer-constant]`

- What is an integer-constant supposed to be?

- What is a cv-list?

- Why can we not deduce `const T` from `T`? (Apparently you only get to deduce if both or neither type have a cv-list, whatever a cv-list is.)

- We have extreme redundancy because, for instance, there is no way to say “in `T (T::*)(T)`, you can deduce any of those `T`s, and it's OK if some of the positions don't have a `T`”. So we have seven (!) forms of that construct, for all cases except the one where none of the three positions contain a `T`.

- We have special case rules for pointers to member functions, even though they're not a special case and should be covered by the rule for pointers to members and the rule for functions.

- We do not allow deducing a template template parameter's value from a template template argument — there is a `TT<T>` form, a `TT<i>` form, a template-name<T> form, and a template-name`<i>` form, but no `TT<TT>` form nor template-name`<TT>` form.

#### 2172. Multiple exceptions with one exception object

Section: 14.4 [except.handle] Status: drafting Submitter: Richard Smith Date: 2015-09-14

During the discussion of issue 2098 it was observed that multiple exceptions may share a single exception object via `std::exception_ptr`. It is not clear that the current wording handles that case correctly.

#### 2219. Dynamically-unreachable handlers

Section: 14.4 [except.handle] Status: drafting Submitter: 2016-01-04 Date: Richard Smith

Consider the following example:

      #include <cstdio>
      #include <cstdlib>

      void f() {
        struct X {
         ~X() {
           std::puts("unwound");
           std::exit(0);
         }
        } x;
        throw 0;
      }

      int main(int argc, char**) {
        try {
          f();
        } catch (int) {
          std::puts("caught");
        }
      }

According to the Standard, this should print `unwound` and exit. Current optimizing implementations call `terminate()`, because:

- `f()` obviously never unwinds (all paths out of it call `exit`)

- Therefore the `catch(int)` clause is unreachable

- Therefore the `catch(int)` clause is removed

- When countering the `throw 0;` statement, the search for a handler finds nothing, so `terminate` is called without unwinding the stack.

More abstractly, before calling `terminate`, we're required to check whether there is an active handler for an exception of type `int`, and in some sense there is not (because the handler in `main` is dynamically unreachable).

There seem to be three possible solutions:

1. Change the standard to say that `terminate()` is a valid response to this situation [this seems problematic, as any non-returning destructor now risks program termination, but is in fact the status quo on multiple implementations and does not seem to have resulted in any bug reports]

2. Always fully unwind before calling terminate() [this significantly harms debugability of exceptions]

3. Teach the compilers to not optimize out unreachable exception handlers [for some implementations, this is “remove, redesign and reimplement middle-end support for EH”-level difficult, and harms the ability to optimize code involving catch handlers]

#### 2003. Zero-argument macros incorrectly specified

Section: 15.7 [cpp.replace] Status: drafting Submitter: Richard Smith Date: 2014-09-12

According to 15.7 [cpp.replace] paragraph 4,

> If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition.

That is, a sequence of no preprocessing tokens counts as an argument. That phrasing has problems with zero-argument function-like macros, e.g.,

      #define M()
      M();

`M` is defined as having no parameters but the invocation has one (empty) argument, which does not match the number of parameters in the definition.

### Issues with "Open" Status

#### 783. Definition of “argument”

Section: Clause 3 [intro.defs] Status: open Submitter: UK Date: 3 March, 2009
N2800 comment UK 3

The definition of an argument does not seem to cover many assumed use cases, and we believe that is not intentional. There should be answers to questions such as: Are lambda-captures arguments? Are type names in a throw-spec arguments? “Argument” to casts, typeid, alignof, alignas, decltype and sizeof? why in x[arg] arg is not an argument, but the value forwarded to operator[]() is? Does not apply to operators as call-points not bounded by parentheses? Similar for copy initialization and conversion? What are deduced template “arguments?” what are “default arguments?” can attributes have arguments? What about concepts, requires clauses and concept_map instantiations? What about user-defined literals where parens are not used?

#### 949. Requirements for freestanding implementations

Section: 4.1 [intro.compliance] Status: open Submitter: Detlef Vollman Date: 2 August, 2009

According to 4.1 [intro.compliance] paragraph 7,

> A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries (16.4.2.5 [compliance]).

This definition links two relatively separate topics: the lack of an operating system and the minimal set of libraries. Furthermore, 6.10.3.1 [basic.start.main] paragraph 1 says:

> [Note: in a freestanding environment, start-up and termination is implementation-defined; start-up contains the execution of constructors for objects of namespace scope with static storage duration; termination contains the execution of destructors for objects with static storage duration. —end note]

It would be helpful if the two characteristics (lack of an operating system and restricted set of libraries) were named separately and if these statements were clarified to identify exactly what is implementation-defined.

Notes from the October, 2009 meeting:

The CWG felt that it needed a specific proposal in a paper before attempting to resolve this issue.

#### 2776. Substitution failure and implementation limits

Section: 4.1.1 [intro.compliance.general] Status: open Submitter: Corentin Jabot Date: 2023-07-27 Liaison: EWG

Are there behavioral bounds in case an implementation limit is encountered? For example, a constant expression may fail to be recognized as such due to an implementation limit (e.g. a recursion or step limit), causing overload resolution to pick a different overload. Is that acceptable?

Possible resolution:

1. Change in 4.1.1 [intro.compliance.general] paragraph 2 as follows:

   > Although this document states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs as specified in the rules in Clause 5 [lex] through Clause 32 [thread] and Annex D. Such requirements have the following meaning:

   > - If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.

   > - Otherwise, if an implementation limit (Annex B) is reached during translation, an implementation shall reject the program.

   >   Recommended practice: An implementation should document limits where they are known, and should issue a diagnostic in cases where it can reasonably detect that the implementation limit is reached.

   > - If a program contains no violations of the rules in Clause 5 [lex] through Clause 32 [thread] and Annex D, a conforming implementation shall, within its resource limits as described in Annex B, accept and correctly execute [ Footnote: ... ] that program.
   > - If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.
   > - Otherwise, if a that program contains a violation of any diagnosable rule 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.
   > - Otherwise, a conforming implementation shall accept and correctly execute that program. [ Note: "Correct execution" can include undefined behavior, depending on the data being processed; see Clause 3 [intro.defs] and 4.1.2 [intro.abstract]. -- end note ]

2. Add a new paragraph after 4.1.2 [intro.abstract] paragraph 6 as follows:

   > [Note 2 : More stringent correspondences between abstract and actual semantics can be defined by each implementation. -- end note]

   > If an implementation limit (Annex B) is reached, the behavior is undefined.

   > Recommended practice: An implementation should document limits where they are known and abort the execution of the program in cases where it can reasonably detect that the implementation limit is reached.

3. Change in 7.7 [expr.const] paragraph 5 as follows:

   > - ...
   > - an expression that would exceed the implementation-defined limits (see Annex B);
   > - an operation that would have undefined behavior as specified in Clause 4 [intro] through Clause 15 [cpp], excluding 9.13.3 [dcl.attr.assume] and exceeding an implementation limit (4.1.2 [intro.abstract]); [ Footnote: ... ]
   >   [ Example:

   >         constexpr bool f(int x) { return (x == 0) || f(x-1); }
   >         const bool g = f(60000);    // well-formed static initialization (6.10.3.2 [basic.start.static]);
   >                                     // program can be rejected because implementation limit is reached

   >   -- end example ]
   > - ...

4. Change in 13.9.2 [temp.inst] paragraph 16 as follows:

   > There is an implementation-defined quantity that specifies the limit on the total depth of recursive instantiations (Annex B), which could involve more than one template. The result of an infinite recursion in instantiation is undefined.

CWG 2023-08-25

For the example in change 3, implementations currently switch to dynamic initialization when their implementation limits related to constant evaluation are exceeded. This might be surprising to users, because dynamic initialization can exhibit order-of-initialization issues. It might be less harmful to reject such situations as ill-formed. Furthermore, it is unclear whether implementations can reliably determine that an implementation limit is reached, before crashing.

CWG is requesting EWG guidance via paper issue #1631.

EWG 2023-11-07

EWG is soliciting a paper to make this either ill-formed, no diagnostic required, or well-formed.

#### 2779. Restrictions on the ordinary literal encoding

Section: 5.3.1 [lex.charset] Status: open Submitter: Jim X Date: 2023-03-28

(From submission #285.)

There are no restrictions on the implementation's choice of ordinary literal encoding. However, there is an implicit assumption that a code unit value must fit into a `char`.

Tangentially related to that, "cannot be encoded as a single code unit" could be interpreted as referring to the values of the code units as opposed to the fact that multiple code units might be needed.

Possible resolution:

1. Change in 5.3.1 [lex.charset] paragraph 8 as follows and add to the index of implementation-defined behavior:

   > A code unit is an integer value of character type (6.9.2 [basic.fundamental]). Characters in a character-literal other than a multicharacter or non-encodable character literal or in a string-literal are encoded as a sequence of one or more code units, as determined by the encoding-prefix (5.13.3 [lex.ccon], 5.13.5 [lex.string]); this is termed the respective literal encoding. The ordinary literal encoding is the implementation-defined encoding applied to an ordinary character or string literal; its code units are of type `unsigned char`. The wide literal encoding is the implementation-defined encoding applied to a wide character or string literal; its code units are of type `wchar_t`.

2. Change in 5.13.3 [lex.ccon] bullet 3.1 as follows:

   > - A character-literal with a c-char-sequence consisting of a single basic-c-char , simple-escape-sequence, or universal-character-name is the code unit value of the specified character as encoded in the literal's associated character encoding. If the specified character lacks representation in the literal's associated character encoding or if it cannot be encoded as a single code unit is encoded with multiple code units, then the program is ill-formed.
   > - ...

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

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

5.5 [lex.pptoken] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 5.8 [lex.operators] paragraph 1, it is unclear whether `new` is an identifier or a preprocessing-op-or-punc; likewise for `delete`. This is relevant to answer the question whether

    #define delete foo

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

(See also issue 189.)

#### 1655. Line endings in raw string literals

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

According to 5.5 [lex.pptoken] 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.1 [lex.charset]) is replaced by the universal-character-name that designates that character.

The reversion described in 5.5 [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?

#### 3019. Restrictions on character sequences in header-names

Section: 5.6 [lex.header] Status: open Submitter: Richard Smith Date: 2025-03-26

Subclause 5.6 [lex.header] specifies implementation-defined behavior for certain character sequences appearing in header-names. This is redundant with the implementation-defined searches specified in 15.3 [cpp.include] and 15.4.1 [cpp.embed.gen], which already cover recognition of `\` escape sequences and similar implementation deviations.

Suggested resolution:

Change in 5.6 [lex.header] as follows:

> The appearance of either of the characters ' or \ or of either of the character sequences /* or // in a q-char-sequence or an h-char-sequence is conditionally-supported with implementation-defined semantics, as is the appearance of the character " in an h-char-sequence. [Note 2: As specified in 15.3 [cpp.include] and 15.4 [cpp.embed], which header, source file, or resource is identified by a header-name, if any, is implementation-defined. Thus, a sequence of characters that resembles an escape sequence can result in an error, be interpreted as the character corresponding to the escape sequence, or have a completely different meaning, depending on the implementation. However, a `"` character cannot appear in an q-char-sequence, and a `>` character cannot appear in a h-char-sequence, even if preceded by a `\` character. —end note]

Additional notes (March, 2025)

The new wording removes the permission for implementations to yield an error when a header-name contains ", \, or ' in a `__has_include`.

#### 189. Definition of operator and punctuator

Section: 5.8 [lex.operators] Status: open Submitter: Mike Miller Date: 20 Dec 1999

The nonterminals operator and punctuator in 5.10 [lex.token] are not defined. There is a definition of the nonterminal operator in 12.4 [over.oper] paragraph 1, but it is apparent that the two nonterminals are not the same: the latter includes keywords and multi-token operators and does not include the nonoverloadable operators mentioned in paragraph 3.

There is a definition of preprocessing-op-or-punc in 5.8 [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.10 [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.8 [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 [over.oper] paragraph 3 among the operators that cannot be overloaded.

(See also issue 369.)

#### 1901. punctuator referenced but not defined

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

The syntactic nonterminal punctuator appears in the grammar for token in 5.10 [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.9 [lex.digraph] paragraph 2 as follows

> In all respects of the language except in an attribute-token (9.13.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...

2. Change the grammar in 5.10 [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

    {    }    [    ]    (    )    ;    :    ...  ?    ::   .    .*
    +    -    *    /    %    ^    &    |    ~    !    =    <    >
    +=   -=   *=   /=   %=   ^=   &=   |=   <<   >>   <<=  >>=
    ==   !=   <=   >=   &&   ||   ++   --   ,    ->*  ->

3. Change 5.10 [lex.token] 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.13.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]

4. Delete the final sentence of 5.8 [lex.operators] paragraph 1.

> Each preprocessing-op-or-punc is converted to a single token in translation phase 7 (5.2 [lex.phases]).

#### 2752. Excess-precision floating-point literals

Section: 5.13.4 [lex.fcon] Status: open Submitter: Peter Dimov Date: 2023-06-29 Liaison: EWG

Consider:

      int main()
      {
        constexpr auto x = 3.14f;
        assert( x == 3.14f );         // can fail?
        static_assert( x == 3.14f );  // can fail?
      }

Can a conforming implementation represent a floating-point literal with excess precision, causing the comparisons to fail?

Subclause 5.13.4 [lex.fcon] paragraph 3 specifies:

> If the scaled value is not in the range of representable values for its type, the program is ill-formed. Otherwise, the value of a floating-point-literal is the scaled value if representable, else the larger or smaller representable value nearest the scaled value, chosen in an implementation-defined manner.

This phrasing leaves little leeway for excess precision. In contrast, C23 specifies in section 6.4.4.3 paragraph 6:

> The values of floating constants may be represented in greater range and precision than that required by the type (determined by the suffix); the types are not changed thereby. ...

Subclause 7.1 [expr.pre] paragraph 6 allows excess precision for floating-point computations (including their operands):

> The values of the floating-point operands and the results of floating-point expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby. [ Footnote: The cast and assignment operators must still perform their specific conversions as described in 7.6.1.4 [expr.type.conv], 7.6.3 [expr.cast], 7.6.1.9 [expr.static.cast] and 7.6.19 [expr.assign]. -- end footnote ]

Taken together, that means that `314.f / 100.f` can be computed and represented more precisely than `3.14f`, which is hard to justify. The footnote appears to imply that `(float)3.14f` is required to yield a value with `float` precision, but that conversion (eventually) ends up at 9.5.1 [dcl.init.general] bullet 16.9:

> - ...
> - Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. ...

If values produced from literals were permitted to carry excess precision, this phrasing does not seem to convery permission to discard excess precision when converting from a `float` value to type `float` ("... is the value..."), apparently requiring that the target object's value also carry the excess precision.

However, if initialization is intended to drop excess precision, then an overloaded operator returning `float` can never behave like a built-in operation with excess precision, because returning a value means initializing the return value.

The C++ standard library inherits the `FLT_EVAL_METHOD` macro from the C standard library. C23 specifies it as follows in section 5.2.5.3.3:

> 0 | evaluate all operations and constants just to the range and precision of the type;
> 1 | evaluate operations and constants of type float and double to the range and precision of the double type, evaluate long double operations and constants to the range and precision of the long double type;
> 2 | evaluate all operations and constants to the range and precision of the long double type.

Taken together, a conforming C++ implementation cannot define `FLT_EVAL_METHOD` to 1 or 2, because literals (= "constants") cannot be represented with excess precision in C++.

Additional notes (June, 2023)

Forwarded to EWG via cplusplus/papers#1584, by decision of the CWG chair.

#### 2766. Repeated evaluation of a string-literal may yield different objects

Section: 5.13.5 [lex.string] Status: open Submitter: Balog Pal Date: 2023-07-15

Prior to the resolution of issue 1823 (incorporated into C++17), the specification required in 9.2.3 [dcl.fct.spec] paragraph 4:

> A string literal in the body of an `extern` `inline` function is the same object in different translation units.

That was changed to the following specification in 5.13.5 [lex.string] paragraph 9:

> Evaluating a string-literal results in a string literal object with static storage duration (6.8.6 [basic.stc]). ... whether successive evaluations of a string-literal yield the same or a different object is unspecified.

The treatment of string literal objects was harmonized with backing arrays in P2752R3 (approved in June, 2023) and issue 2753.

The rationale for the change made by issue 1823 was deviating implementation practice: Implementations do not unify string literal objects in inline functions or template instantiations across different translation units.

However, that was a silent behavior change for examples such as:

      const char *f() { return "abc"; }
      bool b = f() == f();    // guaranteed true prior to CWG1823, now unspecified

The matter should be revisited in light of this new information.

#### 1266. user-defined-integer-literal overflow

Section: 5.13.9 [lex.ext] Status: open Submitter: Michael Wong Date: 2011-03-20

The decimal-literal in a user-defined-integer-literal might be too large for an `unsigned long long` to represent (in implementations with extended integer types). In such cases, the original intent appears to have been to call a raw literal operator or a literal operator template; however, the existing wording of 5.13.9 [lex.ext] paragraph 3 always calls the `unsigned long long` literal operator if it exists, regardless of the value of the decimal-literal.

See also issue 1735.

#### 1723. Multicharacter user-defined character literals

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

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

According to 5.13.9 [lex.ext] paragraph 6,

> If L is a user-defined-character-literal, let ch be the literal without its ud-suffix. S shall contain a literal operator (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 [over.literal] paragraph 3.

Should multicharacter user-defined-character-literals be conditionally-supported? If so, 12.6 [over.literal] paragraph 3 should be adjusted accordingly. If not, a note in 5.13.9 [lex.ext] 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: open 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?

See also issue 1266.

#### 2983. Non-type template parameters are not variables

Section: 6.1 [basic.pre] Status: open Submitter: Richard Smith Date: 2025-01-09

Subclause 6.1 [basic.pre] paragraph 6 specifies:

> A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.

Does this mean that non-type template parameters of class type are variables? Better not.

Possible resolution:

Change in 6.1 [basic.pre] paragraph 6 as follows:

> A non-template-parameter declaration is a declaration that is not a type-parameter or parameter-declaration of a template-parameter. A variable is introduced by the non-template-parameter declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.

#### 2992. Labels do not have names

Section: 6.1 [basic.pre] Status: open Submitter: Vlad Serebrennikov Date: 2025-02-20

(From submission #673.)

Subclause 6.1 [basic.pre] paragraph 3 specifies:

> A name is an identifier (5.11 [lex.name]), conversion-function-id (11.4.8.3 [class.conv.fct]), operator-function-id (12.4 [over.oper]), or literal-operator-id (12.6 [over.literal]).

That seems to incorrectly include identifier labels per the grammar in 8.2 [stmt.label] paragraph 1 as well as attributes per the grammar in 9.13.1 [dcl.attr.grammar] paragraph 1.

Possible resolution:

Change in 6.1 [basic.pre] paragraph 3 as follows and add bullets:

> A name is
> - an identifier (5.11 [lex.name]) other than the identifier of a label or attribute-token,
> - conversion-function-id (11.4.8.3 [class.conv.fct]),
> - operator-function-id (12.4 [over.oper]), or
> - literal-operator-id (12.6 [over.literal]).

#### 1209. Is a potentially-evaluated expression in a template definition a “use?”

Section: 6.3 [basic.def.odr] Status: open Submitter: Johannes Schaub Date: 2010-10-08

Consider the following complete program:

        void f();
        template<typename T> void g() { f(); }
        int main() { }

Must `f()` be defined to make this program well-formed? The current wording of 6.3 [basic.def.odr] does not make any special provision for expressions that appear only in uninstantiated template definitions.

(See also issue 1254.)

#### 2781. Unclear recursion in the one-definition rule

Section: 6.3 [basic.def.odr] Status: open Submitter: Johannes Schaub Date: 2023-08-19

(From submission #402.)

Consider the following tokens appearing in multiple translation units:

     inline void f() {
       int a;
       int b = a;
     }

Does `f` violate the one-definition rule?

We have this check in 6.3 [basic.def.odr] paragraph 14.5:

> - ...
> - In each such definition, corresponding names, looked up according to 6.5 [basic.lookup], shall refer to the same entity, ...
> - ...

Does `a` refer to the same entity in the several definitions of `f`? Subclause 6.3 [basic.def.odr] paragraph 16 specifies:

> These requirements also apply to corresponding entities defined within each definition of D (including the closure types of lambda-expressions, but excluding entities defined within default arguments or default template arguments of either D or an entity not defined within D). For each such entity and for D itself, the behavior is as if there is a single entity with a single definition, including in the application of these requirements to other entities.

Thus, the requirements apply recursively to the definitions of `a`, but it is unclear whether the conclusion "as if there is a single entity with a single definition" is reached at each level of the recursion separately.

Suggested resolution:

Change in 6.3 [basic.def.odr] paragraph 16 as follows:

> If these requirements are satisfied, the behavior for `D` is as if there is a single entity with a single definition, including in the application of these requirements to other entities. These requirements This behavior also apply applies to corresponding entities defined within each definition of D (including the closure types of lambda-expressions, but excluding entities defined within default arguments or default template arguments of either D or an entity not defined within D). For each such entity and for D itself, the behavior is as if there is a single entity with a single definition, including in the application of these requirements to other entities.

#### 2782. Treatment of closure types in the one-definition rule

Section: 6.3 [basic.def.odr] Status: open Submitter: Brian Bi Date: 2023-07-20

Consider:

      inline auto lambda = []{}; // same in different translation units or not?

This can be observed, for example, through the following variable template:

      template<class T> int v;

Is `&v<decltype(lambda)>` the same address in every translation unit?

Possible resolution:

1. Change in 6.3 [basic.def.odr] paragraph 14 as follows:

   > For any definable item D with definitions in multiple translation units,
   > - if D is a non-inline non-templated function or variable, or
   > - if the definitions in different translation units do not satisfy the following requirements,
   > the program is ill-formed; a diagnostic is required only if the definable item is attached to a named module and a prior definition is reachable at the point where a later definition occurs. Given such an item, for all definitions of D, or, if D is an unnamed enumeration, for all definitions of D that are reachable at any given program point, the following requirements shall be satisfied, where the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression.
   > - ...
   > - Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression.
   > - ...

2. Add another example after example 6, immediately before 6.3 [basic.def.odr] paragraph 18:

   > [ Example:

   >       inline decltype([]{}) v1;
   >       inline auto v2 = []{};

   > If the definition of `v1` appears in multiple translation units, the program is ill-formed, no diagnostic required, because each definition declares `v1` to have a different type. If the definition of `v2` appears in multiple translation units, the behavior of the program is as if there is only one definition, and only a definition can supply an initializer; therefore the behavior is as if there is only one initializer. Therefore, `v2` has the same type in every translation unit. -- end example]

   > If, at any point in the program, there is more than one reachable unnamed enumeration definition in the same scope...

#### 2969. Scopes in the function-try-block of a constructor

Section: 6.4 [basic.scope] Status: open Submitter: Richard Smith Date: 2024-12-09

Per the specification, the block scope for a function-try-block does not include the ctor-initializer. Consider the following implementation divergence:

      struct A {
        A() try : p((struct B*)nullptr) {
          B *q;
          struct B {};
          B *r = q;       // EDG, GCC, MSVC: error: cannot convert, two different Bs
        } catch (...) {
          B *q;           // GCC: B not in scope here.
        }
       void *p;
      };

      struct C {
        C(int B) try : p((struct B*)0) {      // GCC: error: shadows parameter; MSVC: error: redefinition of B
        } catch (...) { }
        void *p;
      };

There is also implementation divergence regarding the scope of the ctor-initializer if no function-try-blocks are involved.

#### 2488. Overloading virtual functions and functions with trailing requires-clauses

Section: 6.4.1 [basic.scope.scope] Status: open Submitter: Jiang An Date: 2020-08-19

According to 6.4.1 [basic.scope.scope] paragraph 3,

> 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 parameter-type-list,21 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, the same cv-qualifiers (if any) and ref-qualifier (if both have one), or

> - ...

This would indicate that a virtual function (which cannot have a trailing requires-clause, per 11.7.3 [class.virtual] paragraph 6) can be overloaded with a non-virtual member function with the same parameter type list but with a trailing requires-clause. However, this is not implementable on some ABIs, since the mangling of the two functions would be the same. For example:

      #include <type_traits>
      template<class T>
      struct Foo {
         virtual void fun() const {}
         void fun() const requires std::is_object_v<T> {}
      };
      int main() {
        Foo<int>{}.fun();
      }

Should such overloading be ill-formed or conditionally-supported, or should the current rules be kept?

Rationale (August, 2021):

CWG felt that the current rules are correct; it simply means that only the virtual function can be called, and all other references are simply ambiguous. (See also issue 2501 for a related question dealing with explicit instantiation.

Notes from the November, 2021 teleconference:

The issue has been reopened in response to additional discussion.

CWG 2022-11-11

This is related to issue 2501. CWG solicits a paper to address this issue.

#### 2788. Correspondence and redeclarations

Section: 6.4.1 [basic.scope.scope] Status: open Submitter: Corentin Jabot Date: 2023-08-09

Consider:

      struct S {
        void f() &;
      };
      void S::f(this S&) {}

Both declarations of `S::f` correspond (6.4.1 [basic.scope.scope] paragraph 4), but are ill-formed according to 6.7 [basic.link] paragraph 1, because the functions are not of the same type:

> For any two declarations of an entity E:
> - If one declares E to be a variable or function, the other shall declare E as one of the same type.
> - ...

A similar situation arises for the following example:

      struct S {
        void g() &;
      };
      void S::g() { }

Possible resolution:

Add a note in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

> [ Note: Two function declarations with different ref-qualifiers or parameter-type-lists do not have the same type even if they correspond (6.7 [basic.link]). -- end note ]
> [ Example 2:

>       typedef int Int;
>       ...

>         void m();
>         void n();
>       };

>      X::m() & {}       // error: redeclaration of X::m with a different type
>      X::n(this X&) {}  // error: redeclaration of X::n with a different type

#### 2835. Name-independent declarations

Section: 6.4.1 [basic.scope.scope] Status: open Submitter: Jakub Jelínek Date: 2023-11-30

The wording for name-independent declarations applied by P2169R4 (A nice placeholder with no name) apparently fails to implement the intent.

Consider:

      void foo () { auto c = [_ = 2, _ = 3] () {}; }   // duplicate identifier in lambda-capture

      struct S { int _; int _; };  // repeated member declaration

      void f() {
        if (int _ = 5) {    // #1
          int _ = 6;        // #2
          int k = _;        // ambiguous lookup despite #1 and #2 in different scopes?
        } else {
           int _ = 7;
        }
      }

Possible resolution (first two issues):

1. Change in 7.5.6.3 [expr.prim.lambda.capture] paragraph 2 as follows:

   > ... A capture name is an identifier or `this`. Ignoring appearances in initializers of init-captures, an identifier or `this` shall not appear more than once the program is ill formed if the appearance of a capture name precedes another appearance of the same capture name in a lambda-capture, and the latter is not a name-independent declaration (6.4.1 [basic.scope.scope]). [ Example 1:

   >       struct S2 { void f(int i); };
   >       void S2::f(int i) {
   >         [&, i]{ };  // OK
   >         [&, this, i]{ }; // OK, equivalent to [&, i]
   >         [&, &i]{ };      // error: i preceded by & when & is the default
   >         [=, *this]{ };   // OK
   >         [=, this]{ };    // OK, equivalent to [=]
   >         [i, i]{ };       // error: i repeated
   >         int _ = 17;
   >         [_, _ = 42]{ };      // OK
   >         [this, *this]{ }; // error: this appears twice
   >       }

   > --end example ]

2. Change in 11.4.1 [class.mem.general] paragraph 5 as follows:

   > A member shall not be declared twice in the member-specification, except that
   > - a nested class or member class template can be declared and then later defined, and
   > - an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.
   > [Note 3: A single name can denote several member functions provided their types are sufficiently different (6.4.1 [basic.scope.scope]). Repeated name-independent declarations declare different members. —end note]

CWG 2023-12-15

Having a lookup ambiguity for the third case (`int k = _;`) seems consistent with the idea that #1 and #2 are considered to be in the same scope as far as name conflict detection is concerned. However, that would need new rules, given that name lookup does not proceed to outer scopes when a name is found in an inner scope. Similar lookup ambiguities across nested scopes can arise among lambda captures, template parameters of a lambda, lambda function parameters, and declartions in the outermost block scope of a lambda.

#### 3005. Function parameters should never be name-independent

Section: 6.4.1 [basic.scope.scope] Status: open Submitter: Jan Schultke Date: 2025-03-10

(From submission #681.)

According to P2169R4 (A nice placeholder with no name):

> In Varna, EWG decided not to support function parameter names and requires clauses param-eter as these can remained unamed without semantics differences.

However, the normative wording does permit function parameters to be name-independent, contrary to the express design intent.

Suggested resolution:

Change in 6.4.1 [basic.scope.scope] bullet 5.1 as follows:

> A declaration is name-independent if its name is _ (u+005f low line) and it declares
> - a variable with automatic storage duration other than a function parameter,
> - a structured binding with no storage-class-specifier and not inhabiting a namespace scope,
> - the variable introduced by an init-capture, or
> - a non-static data member of other than an anonymous union.

#### 2838. Declaration conflicts in lambda-expressions

Section: 6.4.3 [basic.scope.block] Status: open Submitter: Jakub Jelínek Date: 2023-11-30

(From submission #475.)

Consider:

      void foo () {
        auto f = [i = 5] () { int i; return 0; };
      }

Before P2579R0 and P2036R3, this was ill-formed, because the capture inhabited the same scope as `int i`. Subclause 6.4.3 [basic.scope.block] paragraph 2 relies on the "parent" scope:

> If a declaration that is not a name-independent declaration and whose target scope is the block scope S of a
> - compound-statement of a lambda-expression, function-body, or function-try-block,
> - substatement of a selection or iteration statement that is not itself a selection or iteration statement, or
> - handler of a function-try-block
> potentially conflicts with a declaration whose target scope is the parent scope of S, the program is ill-formed.

However, after P2579R0, the init-capture inhabits the lambda scope (6.4.5 [basic.scope.lambda]), which is the parent of the parameter scope (6.4.4 [basic.scope.param]), which is the parent of the compound-statement scope, thus the provision does not apply.

As observed in issue 2965, there is no a template parameter scope (6.4.9 [basic.scope.temp]) for a generic lambda:

      auto f = [i = 5] <int N> () { int i; return 0; };

Possible resolution:

Change in 6.4.3 [basic.scope.block] paragraph 2 as follows, adding bullets:

> If a declaration that is not a name-independent declaration and whose target scope is the block scope S of a
> - compound-statement of a lambda-expression, function-body, or function-try-block,
> - substatement of a selection or iteration statement that is not itself a selection or iteration statement, or
> - handler of a function-try-block
> potentially conflicts with a declaration whose target scope is
> - the parent scope of S or,
> - if S is the block scope of a compound-statement of a lambda-expression, the nearest enclosing lambda scope of S,
> the program is ill-formed.

#### 3045. Regularizing environment interactions of expansion statement

Section: 6.4.3 [basic.scope.block] Status: open Submitter: Jakub Jelinek Date: 2025-07-18

(From submission #729.)

Consider:

      template for (auto x : whatever) {
        int x = 42;
      }

This is valid, but the parallel for-range-statement is not:

      for (auto g : whatever) {
        int g = 42;
      }

Furthermore, due to the possibly surprising rewriting nature of expansion statements, 9.13.5 [dcl.attr.fallthrough] should prohibit `[[fallthrough]]`.

Possible suggested resolution:

1. Change in 6.4.3 [basic.scope.block] paragraph 2 as follows:

   > If a declaration that is not a name-independent declaration and that binds a name in the block scope S of a
   > - compound-statement of a lambda-expression, function-body, or function-try-block,
   > - substatement of a selection or , iteration, or expansion statement that is not itself a selection or , iteration, or expansion statement, or
   > - handler of a function-try-block
   > potentially conflicts with a declaration whose target scope is the parent scope of S, the program is ill-formed.

2. Change in 9.13.5 [dcl.attr.fallthrough] paragraph 1 as follows:

   > The attribute-token fallthrough may be applied to a null statement (8.3 [stmt.expr]); such a statement is a fallthrough statement. No attribute-argument-clause shall be present. A fallthrough statement may only appear within an enclosing switch statement (8.5.3 [stmt.switch]). The next statement that would be executed after a fallthrough statement shall be a labeled statement whose label is a case label or default label for the same switch statement and, if the fallthrough statement is contained in an iteration statement, the next statement shall be part of the same execution of the substatement of the innermost enclosing iteration statement. The program is ill-formed if there is no such statement. The innermost enclosing switch statement of a fallthrough statement S shall be contained in the innermost enclosing expansion statement (8.7 [stmt.expand]) of S, if any.

#### 3033. Scope after declarator-id before determining correspondence

Section: 6.4.6 [basic.scope.namespace] Status: open Submitter: Hubert Tong Date: 2025-04-12

(From submission #698.)

Consider:

      namespace N {
        inline namespace A {
          constexpr int f(int *);
        }
        inline namespace B {
          constexpr int f(long *);
        }
      } // namespace N

      template <typename T> struct Q { using type = long; };

      constexpr int N::f(Q<struct X *>::type *) { return 42; } // #1

      namespace N {
        inline namespace B {
          struct X {};
        }
      } // namespace N
      template <> struct Q<N::B::X *>; // explicit specialization after implicit instantiation
      static_assert(N::B::f(0) == 42);

According to 6.4.6 [basic.scope.namespace] paragraph 1, the target scope of the declaration #1 is `N::B`. This is known only after establishing correspondence with the two earlier candidate declarations of `f`. However, a target scope for the elaborated-type-specifier `struct X` at #1 must be determined prior to establishing correspondence.

It would be reasonable to use `N` as the target scope for `struct X`, i.e. the scope named by the nested-name-specififer.

#### 2965. Generic lambdas do not have a template parameter scope

Section: 6.4.9 [basic.scope.temp] Status: open Submitter: Brian Bi Date: 2024-12-03

(From submission #648.)

A generic lambda does not have its own template parameter scope. Add a note to highlight this surprising fact.

Suggested resolution:

Change in 6.4.9 [basic.scope.temp] paragraph 2 as follows:

> Each template-declaration D introduces a template parameter scope that extends from the beginning of its template-parameter-list to the end of the template-declaration. Any declaration outside the template-parameter-list that would inhabit that scope instead inhabits the same scope as D. The parent scope of any scope S that is not a template parameter scope is the smallest scope that contains S and is not a template parameter scope.

> [Note 1: Therefore, only template parameters belong to a template parameter scope, and only template parameter scopes have a template parameter scope as a parent scope. —end note]

> [ Note: The optional template-parameter-list of a lambda-expression (7.5.6 [expr.prim.lambda]) does not introduce a template parameter scope. -- end note ]

#### 2968. Name lookup result for typedef-name vs. class-name

Section: 6.5.1 [basic.lookup.general] Status: open Submitter: CWG Date: 2024-11-21

Consider:

      class C {
      public:
        struct S { };
      private:
        typedef struct S S;
      };
      C::S s;

According to 6.5.1 [basic.lookup.general] paragraph 4:

> In certain contexts, only certain kinds of declarations are included. After any such restriction, any declarations of classes or enumerations are discarded if any other declarations are found. [Note 4: A type (but not a typedef-name or template) is therefore hidden by any other entity in its scope. -- end note] However, if a lookup is type-only, only declarations of types and templates whose specializations are types are considered; furthermore, if declarations of a typedef-name and of the type to which it refers are found, the declaration of the typedef-name is discarded instead of the type declaration.

Thus, the declaration of `s` is ill-formed, because the public class-name `S` is discarded. Clang and EDG concur; GCC and MSVC prefer the public class-name.

Possible resolution:

Change in 6.5.1 [basic.lookup.general] paragraph 4 as follows:

> ...

> However, if a lookup is type-only, only declarations of types and templates whose specializations are types are considered; furthermore, if declarations of a typedef-name and of the type to which it refers are found, the declaration of the typedef-name is discarded instead of the type declaration.

> [ Example:

>       class C {
>       public:
>         struct S { };          // #1
>       private:
>         typedef struct S S;    // #2
>       };
>       struct C::S s;           // OK, type-only lookup, C::S denotes #1, because #2 is discarded
>       C::S s;                  // error: C::S denotes #2, because #1 is discarded

> -- end example ]

#### 3058. "Program point" is not defined

Section: 6.5.1 [basic.lookup.general] Status: open Submitter: Alisdair Meredith Date: 2025-08-13

(From submission #749.)

There is no definition for the term "program point".

Suggested resolution:

Change in 6.5.1 [basic.lookup.general] paragraph 2 as follows:

> There is a program point before the first token of the translation unit, between every pair of adjacent tokens, and after the last token of the translation unit. A program point P is said to follow any declaration in the same translation unit whose locus (6.4.2 [basic.scope.pdecl]) is before P. [Note 1: The declaration might appear in a scope that does not contain P . —end note] ...

#### 380. Definition of "ambiguous base class" missing

Section: 6.5.2 [class.member.lookup] Status: open Submitter: Jason Merrill Date: 22 Oct 2002

The term "ambiguous base class" doesn't seem to be actually defined anywhere. 6.5.2 [class.member.lookup] paragraph 7 seems like the place to do it.

#### 1089. Template parameters in member selections

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

In an example like

        template<typename T> void f(T p)->decltype(p.T::x);

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

        struct T { int x; };
        struct S: T { };

the reference will be ambiguous, since it is looked up in both the context of the expression, finding the template parameter, and in the class, finding the base class injected-class-name, and this could be a deduction failure. As a result, the same declaration with a different parameter name

        template<typename U> void f(U p)->decltype(p.U::x);

is, in fact, not a redeclaration because the two can be distinguished by SFINAE.

It would be better to add a new lookup rule that says that if a name in a template definition resolves to a template parameter, that name is not subject to further lookup at instantiation time.

Additional note (November, 2020):

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

CWG 2023-12-01

Per the status quo rules, `T::x` is a dependent name and thus no lookup occurs at template definition time. At template instantiation time, `T` is first looked up in the class of `p` and, if not found, the template parameter `T` is found. This, in turn, implies that the two templates discussed above are not equivalent, because the first potentially references a `T` member, the other a `U` member of `p`.

Subclause 13.7.7.2 [temp.over.link] paragraph 5 is not in conflict with this interpretation, because no lookup occurs for the `T` token inside the `decltype` at template definition time at all, thus (reliably) "name a template parameter" is not satisfied at that point.

> Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule (6.3 [basic.def.odr]), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression. ...

In order to clarify this, suggestions for a note including the example from this issue are welcome.

#### 2956. Missing allowance for pseudo-destructors in qualified lookup

Section: 6.5.5.1 [basic.lookup.qual.general] Status: open Submitter: CWG Date: 2024-11-08

Subclause 6.5.5.1 [basic.lookup.qual.general] paragraph 1 does not, but should, allow for pseudo-destructors.

Possible resolution:

Change in 6.5.5.1 [basic.lookup.qual.general] paragraph 1 as follows:

> Lookup of an identifier followed by a :: scope resolution operator considers only namespaces, types, and templates whose specializations are types. If a name, template-id, or computed-type-specifier is followed by a ::, it shall designate a namespace, class, enumeration, or dependent type, or if the `::` is followed by `~`, a scalar type, and the :: is never interpreted as a complete nested-name-specifier.

#### 2670. Programs and translation units

Section: 6.7 [basic.link] Status: open Submitter: Gabriel dos Reis Date: 2022-12-21

Subclause 6.7 [basic.link] paragraph 1 specifies:

> A program consists of one or more translation units (5.1 [lex.separate]) linked together. A translation unit consists of a sequence of declarations.

Subclause 5.1 [lex.separate] paragraph 1 defines "translation unit":

> 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, is called a translation unit.

Subclause 5.2 [lex.phases] paragraph 7 first mentions the "translation" of translation units:

> Whitespace characters separating tokens are no longer significant. Each preprocessing token is converted into a token (5.10 [lex.token]). The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.

However, 5.2 [lex.phases] paragraph 8 introduces "instantiation units" and 5.2 [lex.phases] paragraph 9 specifies:

> Translated translation units and instantiation units are combined as follows:

and 5.2 [lex.phases] paragraph 9 specifies:

> All external entity references are resolved. Library components are linked to satisfy external references to entities not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment.

The term "linking" in 5.2 [lex.phases] comes after translated translation units and instantiation units are combined, yet 6.7 [basic.link] paragraph 1 states that (untranslated) translation units are linked to form a program. That seems inconsistent.

Additional notes (February, 2023)

See also issue 2518, which introduced the term "preprocessing translation unit" to remove the conflict between 6.7 [basic.link] paragraph 1 and 5.1 [lex.separate] paragraph 1.

#### 2706. Repeated structured binding declarations

Section: 6.7 [basic.link] Status: open Submitter: Jim X Date: 2023-02-13

Consider:

      struct A {
        int a;
      };
      struct B {
        int b;
      };
      auto&& [x] = A{}; //#1
      auto&& [x] = B{}; //#2

A rule is missing to make such repeated structured binding declarations ill-formed.

Suggested resolution:

Change in 6.7 [basic.link] paragraph 11 as follows:

> For any two declarations of an entity E:
> - If one declares E to be a variable or function, the other shall declare E as one of the same type.
> - If one declares E to be an enumerator, the other shall do so.
> - If one declares E to be a structured binding, the program is ill-formed.
> - ...

#### 2938. Inheriting linkage from a previous declaration

Section: 6.7 [basic.link] Status: open Submitter: Vlad Serebrennikov Date: 2024-09-12

(From submission #609.)

Consider the following example from 6.7 [basic.link] paragraph 6:

      static void f();   // #1
      void q() {
        extern void f(); // #2, internal linkage
      }

#1 has internal linkage per 6.7 [basic.link] bullet 3.1. However, it is unclear why #2 would also get internal linkage. At issue is the phrasing in 6.7 [basic.link] paragraph 4:

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

How does the name matching for "has been given" work here, considering that functions may be overloaded, i.e. different entities may have the same name? The notion of names (as opposed to entities) having linkage is questionable to start with.

CWG 2024-11-08

While the wording is in dire need of improvement here, the intent is that "has been given" uses the "same entity" rules specified in 6.7 [basic.link] paragraph 8. Since both declarations of `f` correspond, have the same target scope, and are in the same translation unit, they refer to the same entity. A rewrite of 6.7 [basic.link] should improve the wording and amend the comment in the example.

#### 2945. Redundant constraints on matching function template declarations

Section: 6.7 [basic.link] Status: open Submitter: Vlad Serebrennikov Date: 2024-10-27

(From submission #630.)

Subclause 6.7 [basic.link] paragraph 11 specifies:

> For any two declarations of an entity E:
> - ...
> - If one declares E to be a function template or a (partial specialization of a) variable template, the other shall declare E to be one with an equivalent template-head and type.
> - ...

However, two function template declarations can only ever declare the same entity if they correspond (6.7 [basic.link] paragraph 8), which requires they have the same template-heads (6.4.1 [basic.scope.scope] paragraph 4). The "equivalent type" requirement covers differences in `noexcept`, which is (intentionally) not covered by "corresponding signatures".

Possible resolution:

Change in 6.7 [basic.link] paragraph 11 as follows:

> For any two declarations of an entity E:
> - ...
> - If one declares E to be a function template, the other shall declare E to be one with an equivalent type.
> - If one declares E to be a function template or a (partial specialization of a) variable template, the other shall declare E to be one with an equivalent template-head and type.
> - ...

#### 2988. Is a closure type from a lambda-expression appearing in a concept-definition a TU-local entity?

Section: 6.7 [basic.link] Status: open Submitter: Jason Merrill Date: 2025-01-24

Consider:

       template<typename _Tp>
        concept __is_derived_from_optional = requires (const _Tp& __t) {
         []<typename _Up>(const optional<_Up>&){ }(__t);
        };

According to 6.7 [basic.link] paragraph 15, the closure type of the lambda-expression is a TU-local entity, thus the concept is an exposure, thus it cannot appear in a module interface unit. There is no fundamental reason why this needs to be the case.

Suggested resolution:

Change in 6.7 [basic.link] paragraph 15.2 as follows:

> An entity is TU-local if it is
> - a type, function, variable, or template that
>   - has a name with internal linkage, or
>   - does not have a name with linkage and is declared, or introduced by a lambda-expression, within the definition of a TU-local entity,
> - a type with no name that is defined outside a class-specifier, function body, concept-definition, or initializer or is introduced by a defining-type-specifier that is used to declare only TU-local entities,
> - ...

#### 2334. Creation of objects by `typeid`

Section: 6.8.2 [intro.object] Status: open Submitter: Chris Hallock Date: 2017-01-30

The list of ways that an object may be created in 6.8.2 [intro.object] paragraph 1 does not include creation of `type_info` objects by `typeid` expressions, but 7.6.1.8 [expr.typeid] does not appear to require that such objects exist before they are referenced. Should the list in 6.8.2 [intro.object] be extended to include this case?

#### 2744. Multiple objects of the same type at the same address

Section: 6.8.2 [intro.object] Status: open Submitter: Chris Hallock Date: 2023-06-08

(From thread beginning here.)

Consider:

      #include <new>

      struct A { unsigned char buf[1]; };
      static_assert(sizeof(A) == 1); // A can fit within A::buf

      int main()
      {
        A x{};
        new (x.buf) A{};
      }

`A::buf` provides storage for another `A` object. Thus, there are now two objects of type `A` within lifetime, which is inconsistent with the goal expressed by 6.8.2 [intro.object] paragraph 9.

Suggested resolution:

Change in 6.8.2 [intro.object] paragraph 3 as follows:

> If a complete object of type T is created (7.6.2.8 [expr.new]) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std::byte” (17.2.1 [cstddef.syn]), that array provides storage for the created object if:
> - the lifetime of e has begun and not ended, and
> - the storage for the new object fits entirely within e, and
> - e is not and is not nested within an object of type similar (7.3.6 [conv.qual]) to `T` that is within its lifetime, and
> - there is no array object that satisfies these constraints nested within e.

#### 2765. Address comparisons between potentially non-unique objects during constant evaluation

Section: 6.8.2 [intro.object] Status: open Submitter: CWG Date: 2023-07-14

The (arguably) expanded treatment of backing arrays and string literals as potentially non-unique objects in issue 2753 lead to the question how the resulting address comparisons are treated during constant evaluation.

Subclause 7.7 [expr.const] bullet 5.24 specifies:

> An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.10.1 [intro.execution]), would evaluate one of the following:
> - ...
> - a three-way comparison (7.6.8 [expr.spaceship]), relational (7.6.9 [expr.rel]), or equality (7.6.10 [expr.eq]) operator where the result is unspecified;
> - ...

This phrasing is understood to refer to explicitly unspecified outcomes only. The treatment of an example such as

      constexpr bool b = "abc" == "abc";

is unclear, given that identical string literals may or may not yield distinct string literal objects.

The assumption that equality comparison of `std::string_view` would compare addresses as a short-cut before comparing the character sequence could not be confirmed (27.2.2 [char.traits.require], 27.3.3.8 [string.view.ops] paragraph 12).

CWG in Tokyo 2024-03-22

Different approaches are feasible:

- Require merging of string literals (and backing arrays of character types?) at compile-time: Comparing addresses of string literals is equivalent to comparing their contents.
- Being able to guarantee that addresses of string literals with different contents are unequal also at compile time seems useful. In particular, this idea would allow comparisons during constant evaluation if (and only if) the runtime result is reliable.
- At compile time, tag each evaluation of a string literal with a globally-unique counter value. Comparing two such pointers with different tags makes the evaluation not a constant expressions.
- At compile time, each string literal (as written in the source code) yields a different address. Repeated evaluations of the same string literal yield pointers that compare equal. This can be achieved by tagging the pointer to the string literal obtained upon evaluation with the source location of the string literal.

In the latter cases, tag values can be preserved when performing pointer arithmetic.

Possible resolution (January, 2025)

1. Add a new paragraph before 6.9.4 [basic.compound] paragraph 4:

   > A pointer value pointing to a potentially non-unique object O (6.8.2 [intro.object]) is associated with the evaluation of the string-literal (5.13.5 [lex.string]) or initializer list (9.5.5 [dcl.init.list]) that resulted in the string literal object or backing array, respectively, that is O or of which O is a subobject. [ Note: A pointer value obtained by pointer arithmetic (7.6.6 [expr.add]) from a pointer value associated with an evaluation E is also associated with E. -- end note ]

   > A pointer value P is valid in the context of an evaluation E if ...

2. Add a bullet after 7.7 [expr.const] bullet 10.25 as follows:

   > An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.10.1 [intro.execution]), would evaluate one of the following:
   > - ...
   > - a three-way comparison (7.6.8 [expr.spaceship]), relational (7.6.9 [expr.rel]), or equality (7.6.10 [expr.eq]) operator where the result is unspecified;
   > - an equality operator comparing pointers to potentially non-unique objects, if the pointer values of both operands are associated with different evaluations (6.9.4 [basic.compound]) and they can both point to the same offset within the same potentially non-unique object; [ Example:

   >         constexpr const char *f() { return "foo"; }

   >         constexpr bool b1 = "foo" == "foo";   // error: non-constant
   >         constexpr bool b2 = f() == f();       // error: non-constant
   >         constexpr const char *p = f();
   >         constexpr bool b3 = p == p;           // OK, value of b3 is true
   >         constexpr bool b4 = "xfoo" + 1 == "foo\0y"; // error: non-constant; string literal object could contain "xfoo\0y"
   >         constexpr bool b5 = "foo" == "bar";   // OK, value of b5 is false
   >         constexpr bool b6 = "foo" == "oo";    // OK, value of b6 is false; offsets would be different in a merged string literal object

   >   -- end example ]
   > - ...

#### 3039. Undefined behavior from implicit object creation ignores observable checkpoints

Section: 6.8.2 [intro.object] Status: open Submitter: Jan Schultke Date: 2025-06-23

(From submission #719.)

Consider:

      int main() {
        unsigned char buffer[1];   // #1, undefined behavior
        std::println("...");       // #2, intended to be observable checkpoint, but also UB
        std::unreachable();        // #3, also undefined behavior
      }

According to 6.8.2 [intro.object] paragraph 11, the implicit object creation at #1 only takes place "if doing so would result in the program having defined behavior", and otherwise the entire program has undefined behavior. However, since #3 arranges for no possible execution to have defined behavior, implicit object creation does not take place, and #1 has undefined behavior, ignoring the observable checkpoint at #2.

Suggested resolution:

1. Add after 4.1.2 [intro.abstract] paragraph 7 as follows:

   > Recommended practice: An implementation should issue a diagnostic when such an operation is executed. [Note 3: ... —end note]

   > Certain operations are specified as making a choice to maximize the defined prefix of the execution. The behavior of all such operations in that execution is such that the defined prefix P of the execution is not a proper subset of the defined prefix that would result from any other choices, but the choices are otherwise unspecified.

2. Change in 6.8.2 [intro.object] paragraph 11 as follows:

   > Some operations are described as implicitly creating objects within a specified region of storage. For each operation O that is specified as implicitly creating objects, that operation O implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types (6.10.1 [intro.execution]) in its specified region of storage if doing so would result in the program having defined behavior chosen to maximize the defined prefix of the execution (4.1.2 [intro.abstract]). If no such set of objects would give the program defined behavior, the behavior of the program is undefined. [Note: If multiple such sets of objects would give the program defined behavior result in a maximal defined prefix for the execution, it is unspecified which such set of objects is created. -- end note]

3. Change in 6.8.2 [intro.object] paragraph 12 as follows:

   > Further, after implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object. These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object , if that value would result in the program having defined behavior. If no such pointer value would give the program defined behavior, the behavior of the program is undefined such as to maximize the defined prefix of the execution. [ Note: If multiple such pointer values would give the program defined behavior result in a maximal prefix, it is unspecified which such pointer value is produced. -- end note]

#### 1211. Misaligned lvalues

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

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

#### 2840. Missing requirements for fundamental alignments

Section: 6.8.3 [basic.align] Status: open Submitter: Jiang An Date: 2023-08-31

(From submission #412.)

Consider:

    struct S {
      long double d;
    };

There appears to be no rule that prevents `S` from having an extended alignment. (See also WG14 DR445.)

Possible resolution:

1. Change in 6.8.3 [basic.align] paragraph 2 as follows:

   > A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to `alignof(std::max_align_t)` (17.2 [support.types]). Fundamental alignments shall be supported by the implementation for objects of all storage durations (6.8.6 [basic.stc]). The alignment required for a type may be different when it is used as the type of a complete object and when it is used as the type of a subobject. ...

2. Change in 6.8.3 [basic.align] paragraph 3 as follows:

   > An extended alignment is represented by an alignment greater than alignof(std::max_align_t). It is implementation-defined whether any extended alignments are supported and the contexts in storage durations for which they are supported (9.13.2 [dcl.align]). A type having an extended alignment requirement is an over-aligned type. [Note 1: Every over-aligned type is or contains a class type to which extended alignment applies (possibly through a non-static data member). —end note] [Note 2: The strictest supported alignment can be different for objects with different storage durations. —end note]

3. Insert a new paragraph before 6.8.3 [basic.align] paragraph 4 and change as follows:

   > Each of the following types has a fundamental alignment requirement:

   > - a scalar type (6.9.1 [basic.types.general]),
   > - an array type (9.3.4.5 [dcl.array]) whose element type has a fundamental alignment requirement, and
   > - a class type (Clause 11 [class]) where all base classes and non-static data members have a type with a fundamental alignment requirement and no non-static data member is declared with an alignment-specifier (9.13.2 [dcl.align]) specifying an extended alignment.

   > Alignments are represented as values of the type `std::size_t`. Valid alignments include only those values returned by an alignof expression for the fundamental types alignments plus an additional implementation-defined set of values, which may be empty. Every alignment value shall be a non-negative integral power of two.

4. Change in 9.13.2 [dcl.align] paragraph 2 as follows:

   > When the alignment-specifier is of the form alignas( constant-expression ):
   > - the constant-expression shall be an integral constant expression and
   > - if the constant expression does not evaluate to an alignment value (6.8.3 [basic.align]), or evaluates to an extended alignment and the implementation does not support that alignment in the context of the declaration for the storage duration (if any) of the entity being declared, the program is ill-formed.

#### 419. Can cast to virtual base class be done on partially-constructed object?

Section: 6.8.4 [basic.life] Status: open Submitter: Judy Ward Date: 2 June 2003

Consider

      extern "C" int printf (const char *,...);

      struct Base { Base();};
      struct Derived: virtual public Base {
         Derived() {;}
      };

      Derived d;
      extern Derived& obj = d;

      int i;

      Base::Base() {
        if ((Base *) &obj) i = 4;
        printf ("i=%d\n", i);
      }

      int main() { return 0; }

11.9.5 [class.cdtor] paragraph 2 makes this valid, but 6.8.4 [basic.life] paragraph 5 implies that it isn't valid.

Steve Adamczyk: A second issue:

      extern "C" int printf(const char *,...);
      struct A                      { virtual ~A(); int x; };
      struct B : public virtual A   { };
      struct C : public B           { C(int); };
      struct D : public C           { D(); };

      int main()                    { D t; printf("passed\n");return 0; }

      A::~A()                       {}
      C::C(int)                     {}
      D::D() : C(this->x)           {}

Core issue 52 almost, but not quite, says that in evaluating "this->x" you do a cast to the virtual base class A, which would be an error according to 11.9.5 [class.cdtor] paragraph 2 because the base class B constructor hasn't started yet. 7.6.1.5 [expr.ref] should be clarified to say that the cast does need to get done.

James Kanze submitted the same issue via comp.std.c++ on 11 July 2003:

> Richard Smith: Nonsense. You can use "this" perfectly happily in a constructor, just be careful that (a) you're not using any members that are not fully initialised, and (b) if you're calling virtual functions you know exactly what you're doing.

In practice, and I think in intent, you are right. However, the standard makes some pretty stringent restrictions in 6.8.4 [basic.life]. To start with, it says (in paragraph 1):

> The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:
> - storage with the proper alignment and size for type T is obtained, and
> - if T is a class type with a non-trivial constructor, the constructor calls has COMPLETED.
> The lifetime of an object of type T ends when:
> - if T is a class type with a non-trivial destructor, the destructor call STARTS, or
> - the storage which the object occupies is reused or released.

(Emphasis added.) Then when we get down to paragraph 5, it says:

> Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [which sounds to me like it would include in the constructor, given the text above] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] If the object will be or was of a non-POD class type, the program has undefined behavior if:

> [...]

> - the pointer is implicitly converted to a pointer to a base class type, or [...]

I can't find any exceptions for the this pointer.

Note that calling a non-static function in the base class, or even constructing the base class in initializer list, involves an implicit conversion of this to a pointer to the base class. Thus undefined behavior. I'm sure that this wasn't the intent, but it would seem to be what this paragraph is saying.

#### 2258. Storage deallocation during period of destruction

Section: 6.8.4 [basic.life] Status: open Submitter: Richard Smith Date: 2016-04-12

What happens if the storage for an object is deallocated in its period of destruction? Consider:

      struct Base {
        virtual ~Base() {
          ::operator delete(this);
        }
        void operator delete(void*) {}
      };

      struct Derived : Base {};

      int main() {
        delete new Derived;
      }

This ought to be undefined behavior, but the standard is silent on the matter.

Notes from the December, 2016 teleconference:

The consensus view was that this should be undefined behavior.

Additional notes (July, 2023)

This issue is a subset of issue 2757.

#### 2514. Modifying const subobjects

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

The change in C++20 for RU007 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.

#### 2676. Replacing a complete object having base subobjects

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

Base subobjects cannot be transparently replaced with complete objects, as specified in 6.8.4 [basic.life] bullet 8.4:

> An object o1 is transparently replaceable by an object o2 if:
> - ...
> - neither o1 nor o2 is a potentially-overlapping subobject (6.8.2 [intro.object]), and
> - ...

However, that bullet is over-reaching, because it disallows:

      struct A { int n; };
      struct B : A {};
      B b;
      new (&b) B { {5} };  // New A base class does not transparently replace existing A base class due to /8.4.
      int k = b.n;  // UB: member n of A base class is outside its lifetime

See issue 2677 for a suggested resolution.

Additional notes (February, 2023)

Consider this example:

      struct A {
       int n;
       char c;
       // tail padding
      };
      struct B {
       [[no_unique_address]] A a;
       char in_tail_padding[3];
      };

      B b;
      void f() {
       // Transparently replaces old member, potentially overwriting the data in the tail padding!
       new (&b.a) A{};
      }

The suggestions do not address this example.

#### 2952. Vacuous initialization for subobjects

Section: 6.8.4 [basic.life] Status: open Submitter: Richard Smith Date: 2024-10-31

Consider:

      struct A {
        int x, y;
        constexpr A() : x((y = 1, y)) {}
      };
      constexpr int k = A().y;

"Vacuous initialization" is defined in 6.8.4 [basic.life] paragraph 1 only for variables, not for subobjects, so it is unclear whether the example is well-formed or not.

Possible resolution:

1. Change in 6.8.4 [basic.life] paragraph 1 as follows:

   > The lifetime of an object or reference is a runtime property of the object or reference. A variable is said to have vacuous initialization if it is default-initialized, no other initialization is performed, and, if it is of class type or a (possibly multidimensional) array thereof, a trivial constructor of that class type is selected for the default-initialization. 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.5 [dcl.init]),
   > except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union (9.5.2 [dcl.init.aggr], 11.9.3 [class.base.init]), or as described in 11.5 [class.union], 11.4.5.3 [class.copy.ctor], and 11.4.6 [class.copy.assign], and except as described in 20.2.10.2 [allocator.members]. ...

2. Change in 6.8.5 [basic.indet] paragraph 1 as follows:

   > ... If no initialization is performed for an object (including subobjects a subobject) is not accessed during its initialization, such a byte retains its initial value until that value is replaced (9.5.1 [dcl.init.general], 7.6.19 [expr.assign]). If any bit in the value representation has an indeterminate value, the object has an indeterminate value; otherwise, if any bit in the value representation has an erroneous value, the object has an erroneous value (7.3.2 [conv.lval]).

3. Change in 8.10 [stmt.dcl] paragraph 2 as follows:

   > A variable is said to have vacuous initialization if it is default-initialized (9.5.1 [dcl.init.general]), no other initialization is performed, and, if it is of class type or a (possibly multidimensional) array thereof, a trivial constructor of that class type (11.4.5.2 [class.default.ctor]) is selected for the default-initialization. A block variable with automatic storage duration (6.8.6.4 [basic.stc.auto]) is active everywhere in the scope to which it belongs after its init-declarator . Upon each transfer of control (including sequential execution of statements) within a function from point P to point Q, all block variables with automatic storage duration that are active at P and not at Q are destroyed in the reverse order of their construction. Then, all block variables with automatic storage duration that are active at Q but not at P are initialized in declaration order; unless all such variables have vacuous initialization (6.8.4 [basic.life]), the transfer of control shall not be a jump. [ Footnote: ... ] When a declaration-statement is executed, P and Q are the points immediately before and after it; when a function returns, Q is after its body.

4. Change in 9.5.1 [dcl.init.general] bullet 7.3 as follows:

   > To default-initialize an object of type T means:
   > - If T is a (possibly cv-qualified) class type (Clause Clause 11 [class]), constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one for the initializer () is chosen through overload resolution (12.2 [over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.
   > - If T is an array type, the semantic constraints of default-initializing a hypothetical element shall be met and each element is default-initialized.
   > - Otherwise, no default initialization is performed completes without accessing the object.

#### 2960. Introduce discontiguous object lifetime

Section: 6.8.4 [basic.life] Status: open Submitter: CWG Date: 2024-11-18

Permitting discontiguous object lifetimes instead of carving out special exceptions for pointers to transparently replaced objects would simplify the wording; when transparemtly replacing an object, the lifetime of the prior objecct would simply start again.

#### 2986. Creating objects within a mutable member of a const object

Section: 6.8.4 [basic.life] Status: open Submitter: Jiang An Date: 2023-07-17

(From submission #367.)

Consider:

      struct S {
        alignas(int) mutable unsigned char buf[sizeof(int)];
      };
      int f() {
        const S s{};
        ::new ((void*)s.buf) int{42};
      }

Creating a new object in the mutable portion of a const complete object is undefined behavior per 6.8.4 [basic.life] paragraph 12. However, this ought to be allowed.

Possible resolution:

Change in 6.8.4 [basic.life] paragraph 12 as follows:

> Creating a new object O within the storage that a const, complete object X with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior unless O is nested within a mutable subobject of X. [ Example: ... ]

#### 3001. Inconsistent restrictions for `static_cast` on pointers to out-of-lifetime objects

Section: 6.8.4 [basic.life] Status: open Submitter: Cody Miller Date: 2025-02-26

According to 6.8.4 [basic.life] paragraph 7, converting a pointer-to-derived to pointer-to-base for an out-of-lifetime object is allowed implicitly, but disallowed using `static_cast`. That seems inconsistent.

Also, the out-of-lifetime rules mishandle this example:

      int a;
      int *p = &a;
      *p = 1;
      new (p) int;

Possible resolution:

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

   > ... Otherwise, if B is a virtual base class of D or a base class of a virtual base class of `D` and v does not point to an object whose type is similar (7.3.6 [conv.qual]) to D and that is within its lifetime or within its period of construction or destruction (11.9.5 [class.cdtor]), the behavior is undefined. ...

2. Change in 6.8.4 [basic.life] paragraph 7 as follows:

   > Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [ Footnote: ...] 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 points to the 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 pointer refers to allocated storage (6.8.6.5.2 [basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type void* is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if
   > - the pointer is used as the operand of a delete-expression,
   > - the pointer is used to access a non-static data member or call a non-static member function of the object, or
   > - the pointer is implicitly converted (7.3.12 [conv.ptr], 7.6.1.9 [expr.static.cast]) to a pointer to a virtual base class or to a base class thereof, or
   > - the pointer is used as the operand of a static_cast (7.6.1.9 [expr.static.cast]), except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char, cv unsigned char, or cv std::byte (17.2.1 [cstddef.syn]), or
   > - the pointer is used as the operand of a dynamic_cast (7.6.1.7 [expr.dynamic.cast]).

#### 3047. Calling destructors on out-of-lifetime objects

Section: 6.8.4 [basic.life] Status: open Submitter: Jiang An Date: 2025-06-18

(From submission #717.)

Calling a non-static member function for an ou8t-of-lifetime object is undefined behavior according to 6.8.4 [basic.life] paragraph 7 and paragraph 8. There is no reason to normatively duplicate that general rule for a subset of destructors in 6.8.4 [basic.life] paragraph 11.

Possible resolution:

Change in 6.8.4 [basic.life] paragraph 11 as follows:

> [Note: If a program ends the lifetime of an object of type T with static (6.8.6.2 [basic.stc.static]), thread (6.8.6.3 [basic.stc.thread]), or automatic (6.8.6.4 [basic.stc.auto]) storage duration and if T has a non-trivial destructor for which the destructor will be called implicitly (8.10 [stmt.dcl], 6.10.3.4 [basic.start.term]), and another object of the original type does not occupy that same storage location when the implicit destructor call takes place, the behavior of the program is undefined. This is true even if the block is exited with an exception. [Example 4: ... —end example] -- end note]

#### 365. Storage duration and temporaries

Section: 6.8.6 [basic.stc] Status: open Submitter: James Kanze Date: 24 July 2002

There are several problems with 6.8.6 [basic.stc]:

- 6.8.6 [basic.stc] paragraph 2 says that "Static and automatic storage durations are associated with objects introduced by declarations (6.2 [basic.def]) and implicitly created by the implementation (6.8.7 [class.temporary])."

  In fact, objects "implicitly created by the implementation" are the temporaries described in (6.8.7 [class.temporary]), and have neither static nor automatic storage duration, but a totally different duration, described in 6.8.7 [class.temporary].

- 6.8.6 [basic.stc] uses the expression "local object" in several places, without ever defining it. Presumably, what is meant is "an object declared at block scope", but this should be said explicitly.

  In a recent discussion in comp.lang.c++.moderated, on poster interpreted "local objects" as including temporaries. This would require them to live until the end of the block in which they are created, which contradicts 6.8.7 [class.temporary]. If temporaries are covered by this section, and the statement in 6.8.6 [basic.stc] seems to suggest, and they aren't local objects, then they must have static storage duration, which isn't right either.

  I propose adding a fourth storage duration to the list after 6.8.6 [basic.stc] paragraph 1:

  - temporary storage duration

  And rewriting the second paragraph of this section as follows:

  > Temporary storage duration is associated with objects implicitly created by the implementation, and is described in 6.8.7 [class.temporary]. Static and automatic storage durations are associated with objects defined by declarations; in the following, an object defined by a declaration with block scope is a local object. The dynamic storage duration is associated with objects created by the `operator new`.

Steve Adamczyk: There may well be an issue here, but one should bear in mind the difference between storage duration and object lifetime. As far as I can see, there is no particular problem with temporaries having automatic or static storage duration, as appropriate. The point of 6.8.7 [class.temporary] is that they have an unusual object lifetime.

Notes from Ocrober 2002 meeting:

It might be desirable to shorten the storage duration of temporaries to allow reuse of them. The as-if rule allows some reuse, but such reuse requires analysis, including noting whether the addresses of such temporaries have been taken.

Notes from the August, 2011 meeting:

The CWG decided that further consideration of this issue would be deferred until someone produces a paper explaining the need for action and proposing specific changes.

See also issue 1634.

#### 1634. Temporary storage duration

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

According to 6.8.6 [basic.stc] 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.8.7 [class.temporary]).

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

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

The latter point is important because 6.8.4 [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.8.7 [class.temporary] and elsewhere that the storage in which a temporary is created is no longer available for reuse, as specified in 6.8.4 [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 365 and 2256.

#### 1682. Overly-restrictive rules on function templates as allocation functions

Section: 6.8.6.5.2 [basic.stc.dynamic.allocation] Status: open Submitter: Jason Merrill Date: 2009-03-03

Requirements for allocation functions are given in 6.8.6.5.2 [basic.stc.dynamic.allocation] paragraph 1:

> An allocation function can be a function template. Such a template shall declare its return type and first parameter as specified above (that is, template parameter types shall not be used in the return type and first parameter type). Template allocation functions shall have two or more parameters.

There are a couple of problems with this description. First, it is instances of function templates that can be allocation functions, not the templates themselves (cf 6.8.6.5.3 [basic.stc.dynamic.deallocation] paragraph 2, which uses the correct terminology regarding deallocation functions).

More importantly, this specification was written before template metaprogramming was understood and hence prevents use of SFINAE on the return type or parameter type to select among function template specializations. (The parallel passage for deallocation functions in 6.8.6.5.3 [basic.stc.dynamic.deallocation] paragraph 2 shares this deficit.)

(See also issue 1628.)

#### 2073. Allocating memory for exception objects

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

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

> [Note: In particular, a global allocation function is not called to allocate storage for objects with static storage duration (6.8.6.2 [basic.stc.static]), for objects or references with thread storage duration (6.8.6.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 [except.throw] paragraph 4. CWG was in favor of retaining the prohibition of using a C++ allocation function for the memory of an exception object, with the implicit understanding that use of `malloc` would be permitted. The resolution for this issue should delete the note and move the prohibition to normative text in the relevant sections.

#### 523. Can a one-past-the-end pointer be invalidated by deleting an adjacent object?

Section: 6.8.6.5.3 [basic.stc.dynamic.deallocation] Status: open Submitter: comp.std.c++ Date: 8 July 2005

When an object is deleted, 6.8.6.5.3 [basic.stc.dynamic.deallocation] says that the deallocation “[renders] invalid all pointers referring to any part of the deallocated storage.” According to 6.9.4 [basic.compound] paragraph 3, a pointer whose address is one past the end of an array is considered to point to an unrelated object that happens to reside at that address. Does this need to be clarified to specify that the one-past-the-end pointer of an array is not invalidated by deleting the following object? (See also 7.6.2.9 [expr.delete] paragraph 4, which also mentions that the system deallocation function renders a pointer invalid.)

#### 3025. Deallocation functions returning void

Section: 6.8.6.5.3 [basic.stc.dynamic.deallocation] Status: open Submitter: Timur Doumler Date: 2025-05-08

Subclause 6.8.6.5.3 [basic.stc.dynamic.deallocation] paragraph 3 specifies:

> Each deallocation function shall return `void`. ...

It has caused confusion in the marketplace whether that is a syntactic constraint on the return type of a deallocation function, or a semantic constraint on the behavior (e.g. a deallocation cannot perform a `longjmp`).

Possible resolution:

Change in 6.8.6.5.3 [basic.stc.dynamic.deallocation] paragraph 3 as follows:

> Each A deallocation function shall have a declared return type of type `void`. ...

#### 2666. Lifetime extension through `static_cast`

Section: 6.8.7 [class.temporary] Status: open Submitter: Brian Bi Date: 2022-10-17

Consider:

      struct S { S(int) {} };

      const S& r1 = 0; // #1
      const S& r2 = static_cast<const S&>(S(0)); // #2
      const S& r3 = static_cast<const S&>(0); // #3
      // Are r1, r2, and r3 dangling past this point?

For #1, a temporary object is materialized and the reference binds to the result of the materialization conversion. This results in lifetime extension per 6.8.7 [class.temporary] bullet 6.1.

For #2, 6.8.7 [class.temporary] bullet 6.6 requires the cast to convert "without a user-defined conversion", but `S(0)` is such.

For #3, a user-defined conversion is clearly required.

However, implementations treat all three cases the same and do extend the lifetime of the temporary.

Suggested resolution:

Change in 6.8.7 [class.temporary] bullet 6.6 as follows:

> - ...
> - a
>   - const_cast (7.6.1.11 [expr.const.cast]),
>   - static_cast (7.6.1.9 [expr.static.cast]),
>   - dynamic_cast (7.6.1.7 [expr.dynamic.cast]), or
>   - reinterpret_cast (7.6.1.10 [expr.reinterpret.cast])
>   converting, without a user-defined conversion, a glvalue operand that is one of these expressions to a glvalue that refers to the object designated by the operand, or to its complete object or a subobject thereof, where
>   - the cast does not invoke a user-defined conversion, or
>   - the cast invokes a user-defined conversion that yields a prvalue, and the glvalue result of the cast is bound directly (9.5.4 [dcl.init.ref]) to that prvalue,
> - ...

Additional notes (October, 2024)

The suggested resolution does not use the "is one of these expressions" structure that limits the tracking of temporary objects through the expression tree required from implementation. For example:

      int glob;
      struct A {
       constexpr ~A() { arr[0] = &glob; }
       int *arr[1];
      };
      constexpr int f() {
       int *&&ref = static_cast<int *&&>(0[+A().arr]); // note unary +; lifetime is not extended
       delete ref; // error: attempt to delete `&glob`
       return 0;
      }
      extern constexpr int x = f();

Suggested resolution:

Change in 6.8.7 [class.temporary] bullet 6.6 as follows:

> - ...
> - a
>   - const_cast (7.6.1.11 [expr.const.cast]),
>   - static_cast (7.6.1.9 [expr.static.cast]),
>   - dynamic_cast (7.6.1.7 [expr.dynamic.cast]), or
>   - reinterpret_cast (7.6.1.10 [expr.reinterpret.cast])
>   converting, without a user-defined conversion, a glvalue operand that is one of these expressions to a glvalue that refers to the object designated by the operand, or to its complete object or a subobject thereof, expression where
>   - the operand is one of these expressions and the cast does not invoke a user-defined conversion, or
>   - the result of the cast refers to an object created by a temporary materialization conversion that is required or performed by the cast, or to a subobject of such an object, where
>     - the cast does not invoke a user-defined conversion, or
>     - the temporary materialization conversion is applied to the prvalue result of a user-defined conversion that is applied to the operand.
> - ...

#### 2832. Invented temporary variables and temporary objects

Section: 6.8.7 [class.temporary] Status: open Submitter: Thomas Köppe Date: 2023-11-09

(From editorial issue 6646.)

Both 7.3.1 [conv.general] and 7.6.1.9 [expr.static.cast] introduce "invented temporary variables", but it is unclear whether the corresponding objects are temporary objects with lifetimes as specified in 6.8.7 [class.temporary].

Possible resolution:

1. Change in 6.8.7 [class.temporary] as follows:

2. Change in 7.3.1 [conv.general] paragraph 3 and paragraph 4 as follows, moving the declarations into a code block:

   > 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` (6.8.7 [class.temporary], 9.5 [dcl.init]).

   > Certain language constructs require that an expression be converted to a Boolean value. An expression E appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration

   >       bool t(E);

   > is well-formed, for some invented temporary variable `t` (6.8.7 [class.temporary], 9.5 [dcl.init]).

3. Change in 7.6.1.9 [expr.static.cast] as follows:

   > ... If T is a reference type, the effect is the same as performing the declaration and initialization

   >       T t(E);

   > for some invented temporary variable `t` (6.8.7 [class.temporary], 9.5 [dcl.init]) and then using the temporary variable as the result of the conversion. ...

#### 2868. Self-references in trivially copyable objects as function return values

Section: 6.8.7 [class.temporary] Status: open Submitter: Richard Smith Date: 2018-02-16

Consider:

      struct A {
        A *children;
        long long arr[100];

        A() : children() {}
        A(int) : children(this) {}
      };

      __attribute__((noinline))
      A Foo() {
        return A(0);   // #1
      }

      A x[3] = {};

      void Bar(int n) {
        A a = Foo();
        for (int i = 0; i < n; i++) {
          a.children[i].children = x;     // #2
        }
      }

      int main() {
        Bar(3);
        return x[0].children || !x[1].children || !x[2].children;
      }

It ought to be valid to hoist the load of `a.children` out of the loop. However, guaranteed copy elision is required to apply to #1, making the `A` objects in `Foo` and `Bar` be the same object. An additional temporary copy can be made per 6.8.7 [class.temporary] paragraph 3, but that does not cause the `children` member of the `a` variable to become invalid.

Suggested resolution:

Change in 6.8.7 [class.temporary] paragraph 3 as follows:

> When an object of class type X is passed to or returned from a function, if X has at least one eligible copy or move constructor (11.4.4 [special]), each such constructor is trivial, and the destructor of X is either trivial or deleted, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the eligible trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). The temporary object may occupy the storage that the parameter or return object is going to occupy, but pointers that pointed to the temporary object become invalid pointer values (6.9.4 [basic.compound]) when the parameter or return object is constructed and do not point to the parameter or return object.

CWG 2024-05-03

For return values, CWG favors replacing the exception in 6.8.7 [class.temporary] paragraph 3 with an amendment to the specification of guaranteed copy elision in 9.5.1 [dcl.init.general] bullet 16.6.1. It is unclear whether parameter values should be treated the same.

Possible resolution:

1. Change in 6.8.7 [class.temporary] paragraph 3 as follows:

   > When an object of class trivially returnable (8.8.4 [stmt.return]) type X is passed to or returned from a function, if X has at least one eligible copy or move constructor (11.4.4 [special]), each such constructor is trivial, and the destructor of X is either trivial or deleted, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the eligible trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).

2. Split and change 8.8.4 [stmt.return] paragraph 2 as follows:

   > The expr-or-braced-init-list of a return statement is called its operand. A return statement with no operand shall be used only in a function whose return type is cv void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]). A return statement with an operand of type void shall be used only in a function that has a cv void return type. A return statement with any other operand shall be used only in a function that has a return type other than cv void; the return statement initializes the returned reference or prvalue result object of the (explicit or implicit) function call by copy-initialization (9.5 [dcl.init]) from the operand. [Note 1: A constructor or destructor does not have a return type. —end note]

   > A type `T` is trivially returnable if `T` is a class type that has at least one eligible copy or move constructor (11.4.4 [special]), each such constructor is trivial, and the destructor of `T` is either trivial or deleted. If a function has a return type `R` other than cv `void`:

   > - If `R` is trivially returnable, the return statement initializes a first temporary object from the operand (7.3.5 [conv.rval]). Then, a second temporary object is constructed from the first temporary object, using an eligible trivial constructor for the copy. Then, the first temporary object is destroyed and the duration of its storage ends. [ Note: A pointer value pointing to the first temporary object becomes an invalid pointer value (6.9.4 [basic.compound]). -- end note ] Finally, the prvalue result object of the function call is initialized from the second temporary object, using an eligible trivial constructor for the copy, and the second temporary object is destroyed. In both cases, an eligible trivial constructor is used even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object.
   > - Otherwise, the return statement initializes the returned reference or prvalue result object of the (explicit or implicit) function call by copy-initialization (9.5 [dcl.init]) from the operand.
   > [ Note 2: A return statement can involve an invocation of a constructor to perform a copy or move of the operand if it is not a prvalue or if its type differs from the return type of the function. A copy operation associated with a return statement can be elided or converted to a move operation if an automatic storage duration variable is returned (11.9.6 [class.copy.elision]). —end note]

#### 2941. Lifetime extension for function-style cast to reference type

Section: 6.8.7 [class.temporary] Status: open Submitter: Hubert Tong Date: 2024-10-18

(From submission #625.)

Issue 2894 will clarify that a function-style cast to reference type produces a glvalue, not a prvalue. However, 6.8.7 [class.temporary] does not specify lifetime-extension for this case, even though implementations uniformly do extend the lifetime.

For example:

      int glob;
      struct A {
       constexpr ~A() { p = &glob; }
       int *p;
      };
      constexpr int f() {
       typedef const A &AR;
       const A &ref = AR{0};
       delete ref.p;
       return 0;
      }
      extern constexpr int x = f(); // okay

Suggested resolution:

Change in 6.8.7 [class.temporary] paragraph 6 as follows:

> - a
>   - const_cast (7.6.1.11 [expr.const.cast]),
>   - ...
> - an explicit type conversion (functional notation) (7.6.1.4 [expr.type.conv]) to a reference type whose initializer is a braced-init-list where the reference is
>   - bound directly to the glvalue result of one of these expressions that is the sole element of the braced-init-list or
>   - bound to the result of a temporary materialization conversion,
> - a conditional expression (7.6.16 [expr.cond]) that is a glvalue where the second or third operand is one of these expressions, or

#### 3043. Lifetime extension for temporaries in expansion statements

Section: 6.8.7 [class.temporary] Status: open Submitter: Jakub Jelinek Date: 2025-07-07

(From submission #724.)

Subclause 6.8.7 [class.temporary] paragraph 7 and paragraph 8 specify:

> The fourth context is when a temporary object is created in the for-range-initializer of either a range-based for statement or an enumerating expansion statement (8.7 [stmt.expand]). If such a temporary object would otherwise be destroyed at the end of the for-range-initializer full-expression, the object persists for the lifetime of the reference initialized by the for-range-initializer.

> The fifth context is when a temporary object is created in the expansion-initializer of an iterating or destructuring expansion statement. If such a temporary object would otherwise be destroyed at the end of that expansion-initializer , the object persists for the lifetime of the reference initialized by the expansion-initializer, if any.

There are a number of problems with this phrasing:

- The original idea of lifetime extension in a for-range-initializer was to ensure that temporaries defining a range would survive across all iterations. For an enumerating expansion statement, there is no overarching range expression; each element of the expression-list is considered separately. Furthermore, there is no for-range-initializer in the corresponding grammar to start with.
- For an iterating expansion statement, the involved variables are `constexpr`, so no dangling pointers or similar could escape from the constant evaluation to start with. The necessity for temporary lifetime extension is thus unclear.

Possible resolution:

Change in 6.8.7 [class.temporary] paragraph 7 and paragraph 8 as follows:

> The fourth context is when a temporary object is created in the for-range-initializer of either a range-based for statement or an enumerating expansion statement (8.7 [stmt.expand]). If such a temporary object would otherwise be destroyed at the end of the for-range-initializer full-expression, the object persists for the lifetime of the reference initialized by the for-range-initializer.

> The fifth context is when a temporary object is created in the expansion-initializer of an iterating or a destructuring expansion statement. If such a temporary object would otherwise be destroyed at the end of that expansion-initializer, the object persists for the lifetime of the reference initialized by the expansion-initializer, if any.

#### 3063. Lifetime extension of temporaries past function return

Section: 6.8.7 [class.temporary] Status: open Submitter: Hubert Tong Date: 2025-09-12

(From submission #760.)

Consider:

      struct B { ~B(); };
      struct A { const B &b; };
      A foo() { return {{}}; }   // #1
      void bar();
      int main() {
        A a = foo();
        bar();
      }

At #1, a temporary of type `B` is created and, due to guaranteed copy elision, is bound to `a.b` in `main`. The current rules, as amended by P2748R5 (Disallow Binding a Returned Glvalue to a Temporary), prescribe lifetime extension of that temporary, which is a novel requirement accidentally imposed by P2748R5 that is hard to implement.

Suggested resolution:

Add in 6.8.7 [class.temporary] bullet 6.11 as follows:

> - ...
> - A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list (9.5 [dcl.init]) persists until the completion of the full-expression containing the expression-list.
> - The lifetime of a temporary bound to a reference member of the returned value in a function `return` statement (8.8.4 [stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the `return` statement.
> - A temporary bound to a reference in a new-initializer (7.6.2.8 [expr.new]) persists until ...

#### 350. `signed char` underlying representation for objects

Section: 6.9 [basic.types] Status: open Submitter: Noah Stein Date: 16 April 2002 Liaison: WG14

Sent in by David Abrahams:

Yes, and to add to this tangent, 6.9.2 [basic.fundamental] paragraph 1 states "Plain char, signed char, and unsigned char are three distinct types." Strangely, 6.9 [basic.types] paragraph 2 talks about how "... the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value." I guess there's no requirement that this copying work properly with signed chars!

Notes from October 2002 meeting:

We should do whatever C99 does. 6.5p6 of the C99 standard says "array of character type", and "character type" includes signed char (6.2.5p15), and 6.5p7 says "character type". But see also 6.2.6.1p4, which mentions (only) an array of unsigned char.

Proposed resolution (April 2003):

Change 6.8.4 [basic.life] bullet 5.3 from

- the pointer is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) (except when the conversion is to `void*`, or to `void*` and subsequently to `char*`, or `unsigned` `char*`).

to

- the pointer is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) (except when the conversion is to `void*`, or to `void*` and subsequently to a pointer to byte-character type).

Change 6.8.4 [basic.life] bullet 6.3 from

- the lvalue is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) (except when the conversion is ultimately to `char&` or `unsigned` `char&`), or

to

- the lvalue is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) (except when the conversion is ultimately to a reference to byte-character type), or

Change the beginning of 6.9 [basic.types] paragraph 2 from

> For any object (other than a base-class subobject) of POD type `T`, whether or not the object holds a valid value of type `T`, the underlying bytes (6.8.1 [intro.memory]) making up the object can be copied into an array of `char` or `unsigned` `char`.

to

> For any object (other than a base-class subobject) of POD type `T`, whether or not the object holds a valid value of type `T`, the underlying bytes (6.8.1 [intro.memory]) making up the object can be copied into an array of byte-character type.

Add the indicated text to 6.9.2 [basic.fundamental] paragraph 1:

> Objects declared as characters (`char`) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a `char` object can hold negative values. Characters can be explicitly declared `unsigned` or `signed`. Plain `char`, `signed char`, and `unsigned char` are three distinct types, called the byte-character types. A `char`, a `signed char`, and an `unsigned char` occupy the same amount of storage and have the same alignment requirements (6.9 [basic.types]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned byte-character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain `char` object can take on either the same values as a `signed char` or an `unsigned char`; which one is implementation-defined.

Change 7.2.1 [basic.lval] paragraph 15 last bullet from

- a `char` or `unsigned` `char` type.

to

- a byte-character type.

Notes from October 2003 meeting:

It appears that in C99 signed char may have padding bits but no trap representation, whereas in C++ signed char has no padding bits but may have -0. A memcpy in C++ would have to copy the array preserving the actual representation and not just the value.

March 2004: The liaisons to the C committee have been asked to tell us whether this change would introduce any unnecessary incompatibilities with C.

Notes from October 2004 meeting:

The C99 Standard appears to be inconsistent in its requirements. For example, 6.2.6.1 paragraph 4 says:

> The value may be copied into an object of type `unsigned char [`n`]` (e.g., by `memcpy`); the resulting set of bytes is called the object representation of the value.

On the other hand, 6.2 paragraph 6 says,

> If a value is copied into an object having no declared type using `memcpy` or `memmove`, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.

Mike Miller will investigate further.

Proposed resolution (February, 2010):

1. Change 6.8.4 [basic.life] bullet 5.4 as follows:

> ...The program has undefined behavior if:

> - ...

> - the pointer is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) (except when the conversion is to cv `void*`, or to cv `void*` and subsequently to `char*`, or `unsigned char*` a pointer to a cv-qualified or cv-unqualified byte-character type (6.9.2 [basic.fundamental])), or

> - ...

2. Change 6.8.4 [basic.life] bullet 6.4 as follows:

> ...The program has undefined behavior if:

> - ...

> - the lvalue is used as the operand of a `static_cast` (7.6.1.9 [expr.static.cast]) except when the conversion is ultimately to cv `char&` or cv `unsigned char&` a reference to a cv-qualified or cv-unqualified byte-character type (6.9.2 [basic.fundamental]) or an array thereof, or

> - ...

3. Change 6.9 [basic.types] paragraph 2 as follows:

> For any object (other than a base-class subobject) of trivially copyable type `T`, whether or not the object holds a valid value of type `T`, the underlying bytes (6.8.1 [intro.memory]) making up the object can be copied into an array of `char` or `unsigned char` a byte-character type (6.9.2 [basic.fundamental]).39 If the content of the that array of `char` or `unsigned char` is copied back into the object, the object shall subsequently hold its original value. [Example:...

4. Change 6.9.2 [basic.fundamental] paragraph 1 as follows:

> ...Characters can be explicitly declared `unsigned` or `signed`. Plain `char`, `signed char`, and `unsigned char` are three distinct types, called the byte-character types. A `char`, a `signed char`, and an `unsigned char` occupy the same amount of storage and have the same alignment requirements (6.8.3 [basic.align]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned character types `unsigned char`, all possible bit patterns of the value representation represent numbers...

5. Change 7.2.1 [basic.lval] paragraph 15 final bullet as follows:

> If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined 52

> - ...

> - a `char` or `unsigned char` byte-character type (6.9.2 [basic.fundamental]).

6. Change 6.8.3 [basic.align] paragraph 6 as follows:

> The alignment requirement of a complete type can be queried using an `alignof` expression (7.6.2.6 [expr.alignof]). Furthermore, the byte-character types (6.9.2 [basic.fundamental]) `char`, `signed char`, and `unsigned char` shall have the weakest alignment requirement. [Note: this enables the byte-character types to be used as the underlying type for an aligned memory area (9.13.2 [dcl.align]). —end note]

7. Change 7.6.2.8 [expr.new] paragraph 10 as follows:

> ...For arrays of `char` and `unsigned char` a byte-character type (6.9.2 [basic.fundamental]), the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (6.8.3 [basic.align]) of any object type whose size is no greater than the size of the array being created. [Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating byte-character arrays into which objects of other types will later be placed. —end note]

Notes from the March, 2010 meeting:

The CWG was not convinced that there was a need to change the existing specification at this time. Some were concerned that there might be implementation difficulties with giving `signed char` the requisite semantics; implementations for which that is true can currently make `char` equivalent to `unsigned char` and avoid those problems, but the suggested change would undermine that strategy.

Additional note, November, 2014:

There is now the term “narrow character type” that should be used instead of “byte-character type”.

#### 1701. Array vs sequence in object representation

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

According to 6.9 [basic.types] paragraph 4,

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

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

Additional note, November, 2014:

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

#### 2953. Value representation for non-trivially-copyable types

Section: 6.9.1 [basic.types.general] Status: open Submitter: Brian Bi Date: 2024-11-08

(From submission #635.)

Subclause 6.9.1 [basic.types.general] paragraph 4 specifies:

> ... The value representation of a type T is the set of bits in the object representation of T that participate in representing a value of type T. ... For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, ... [ Footnote: ... ]

It is unclear what difference is intended between "participate in representing a value" and "determines a value". If there is no difference, it is unclear why one of the statements applies only to trivially copyable types. The standard does not seem to refer to the value representation of types that are not trivially copyable.

Suggested resolution:

Change in 6.9.1 [basic.types.general] paragraph 4 as follows:

> The object representation of a complete object type T is the sequence of N unsigned char objects taken up by a non-bit-field complete object of type T, where N equals sizeof(T). The value representation of a trivially copyable type T is the set of bits in the object representation of T that participate in representing a value value of type T, which is one discrete element of an implementation-defined set of values. The object and value representation of a non-bit-field complete object of type T are the bytes and bits, respectively, of the object corresponding to the object and value representation of its type. The object representation of a bit-field object is the sequence of N bits taken up by the object, where N is the width of the bit-field (11.4.10 [class.bit]). The value representation of a bit-field object is the set of bits in the object representation that participate in representing its value. Bits in the object representation of a type or object that are not part of the value representation are padding bits. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. [ Footnote: ... ]

#### 146. Floating-point zero

Section: 6.9.2 [basic.fundamental] Status: open Submitter: Andy Sawyer Date: 23 Jul 1999

6.9.2 [basic.fundamental] does not impose a requirement on the floating point types that there be an exact representation of the value zero. This omission is significant in 7.3.14 [conv.fctptr] paragraph 1, in which any non-zero value converts to the `bool` value `true`.

Suggested resolution: require that all floating point types have an exact representation of the value zero.

#### 251. How many signed integer types are there?

Section: 6.9.2 [basic.fundamental] Status: open Submitter: Beman Dawes Date: 18 Oct 2000

6.9.2 [basic.fundamental] paragraph 2 says that

> There are four signed integer types: "`signed char`", "`short int`", "`int`", and "`long int`."

This would indicate that `const int` is not a signed integer type.

Notes from the June, 2016 meeting:

See issue 2185.

#### 2966. Alignment and value representation of `std::nullptr_t`

Section: 6.9.2 [basic.fundamental] Status: open Submitter: Jiang An Date: 2024-11-22

(From submission #644.)

While 6.9.2 [basic.fundamental] paragraph 16 specifies the size of `std::nullptr_t`, it is silent on any alignment requirements. Furthermore, unlike in C23, there is no statement about the value representation of `std::nullptr_t`.

Possible resolution:

Change in 6.9.2 [basic.fundamental] paragraph 16 as follows:

> The types denoted by cv 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 size (7.6.2.5 [expr.sizeof]) and alignment requirement (6.8.3 [basic.align]) of the type `std::nullptr_t` are those of the type "pointer to `void`". [ Note: The value representation can comprise no bits (7.3.2 [conv.lval]). -- end note ]

#### 3036. Extended floating-point types should not be cv-qualified

Section: 6.9.3 [basic.extended.fp] Status: open Submitter: Jan Schultke Date: 2025-06-03

(From submission #710.)

It is underspecified whether `std::float16_t` and related extended floating-point types are cv-qualified or not. The intent is that those are cv-unqualified.

Possible resolution:

Change in 17.4.2 [stdfloat.syn] as follows:

      namespace std {
        #if defined(__STDCPP_FLOAT16_T__)
          using float16_t = implementation-defined decltype(0.0f16) ; // see 6.9.3 [basic.extended.fp]
        #endif
        #if defined(__STDCPP_FLOAT32_T__)
          using float32_t = implementation-defined decltype(0.0f32) ; // see 6.9.3 [basic.extended.fp]
        #endif
        #if defined(__STDCPP_FLOAT64_T__)
          using float64_t = implementation-defined decltype(0.0f64) ; // see 6.9.3 [basic.extended.fp]
        #endif
        #if defined(__STDCPP_FLOAT128_T__)
          using float128_t = implementation-defined decltype(0.0f128) ; // see 6.9.3 [basic.extended.fp]
        #endif
        #if defined(__STDCPP_BFLOAT16_T__)
          using bfloat16_t = implementation-defined decltype(0.0bf16) ; // see 6.9.3 [basic.extended.fp]
        #endif
      }

#### 2544. Address of past-the-end of a potentially-overlapping subobject

Section: 6.9.4 [basic.compound] Status: open Submitter: Jiang An Date: 2022-02-20

6.9.4 [basic.compound] paragraph 3 states:

> A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory (6.8.1 [intro.memory]) occupied by the object [ Footnote: ... ] or the first byte in memory after the end of the storage occupied by the object, respectively.

A potentially-overlapping subobject of type `T` may occupy fewer bytes than indicated by `sizeof(T)`, yet pointer arithmetic will only consider `sizeof(T)`, not the number of actually occupied bytes. For example,

    struct X {
      X() = default;
      int x;
      short y;
    };

    struct S {
      [[no_unique_address]] X x;
      short z;
    };

    static_assert(sizeof(X) == sizeof(S));

On a popular implementation, `z` is actually put into the tail padding of `x`, and thus `&S().x + 1` does not actually point to "the first byte in memory after the end of the storage occupied by" `x`.

Suggested resolution (amended 2022-03-10):

Change in 6.9.4 [basic.compound] paragraph 3 as follows:

> A value V of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory (6.8.1 [intro.memory]) occupied by the object A as follows:

> - If V is a pointer to an object, A is the address of the object as specified in 6.8.2 [intro.object] [ Footnote: ... ] or the first byte in memory after the end of the storage occupied by the object, respectively.
> - If V is a pointer past the end of an object that is not a potentially-overlapping subobject, A is the address of the first byte in memory after the end of the storage occupied by the object.
> - If V is a pointer past the end of a potentially-overlapping subobject, A is an implementation-defined choice of an address within the complete object or the address of the first byte in memory after the end of the storage occupied by the complete object.
> - If V is a null pointer value or an invalid pointer value, V does not represent an address.

CWG 2022-11-11

Unify the treatment of all subobjects by saying the pointer past-the-end has address `address(begin) + sizeof(T)`.

#### 698. The definition of “sequenced before” is too narrow

Section: 6.10.1 [intro.execution] Status: open Submitter: Jens Maurer Date: 13 July, 2008

According to 6.10.1 [intro.execution] paragraph 14, “sequenced before” is a relation between “evaluations.” However, 6.10.3.3 [basic.start.dynamic] paragraph 3 says,

> If the completion of the initialization of a non-local object with static storage duration is sequenced before a call to `std::atexit` (see `<cstdlib>`, 17.5 [support.start.term]), the call to the function passed to `std::atexit` is sequenced before the call to the destructor for the object. If a call to `std::atexit` is sequenced before the completion of the initialization of a non-local object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to `std::atexit`. If a call to `std::atexit` is sequenced before another call to `std::atexit`, the call to the function passed to the second `std::atexit` call is sequenced before the call to the function passed to the first `std::atexit` call.

Except for the calls to `std::atexit`, these events do not correspond to “evaluation” of expressions that appear in the program. If the “sequenced before” relation is to be applied to them, a more comprehensive definition is needed.

#### 2955. Unify rules about conflicting unordered accesses

Section: 6.10.1 [intro.execution] Status: open Submitter: CWG Date: 2024-11-08

The rules in 6.10.1 [intro.execution] paragraph 10 (within a single thread) and 6.10.2.2 [intro.races] paragraph 2 (for concurrent evaluations) are strikingly similar and should be merged.

#### 1842. Unevaluated operands and “carries a dependency”

Section: 6.10.2 [intro.multithread] Status: open Submitter: Hans Boehm Date: 2014-01-23 Liaison: SG1

According to 6.10.2 [intro.multithread] paragraph 9,

> An evaluation A carries a dependency to an evaluation B if

> - the value of A is used as an operand of B, unless:

> - ...

> - A is the left operand of a built-in logical AND (`&&,` see 7.6.14 [expr.log.and]) or logical OR (`||`, see 7.6.15 [expr.log.or]) operator, or

> - ...

The intent is that this does not apply to the second operands of such operators if the first operand is such that they are not evaluated, but the wording is not clear to that effect. (A similar question applies to the non-selected operand of the conditional operator `?:`.)

Notes from the October, 2015 meeting:

It appears likely that the text involved will be removed by a revision to the `memory_order_consume` specification.

Notes from the February, 2016 meeting:

Action on this issue will be deferred until the specification for `memory_order_consume` is complete; it should not currently be used.

#### 2297. Unclear specification of atomic operations

Section: 6.10.2.2 [intro.races] Status: open Submitter: Kazutoshi Satoda Date: 2016-01-21

It is not sufficiently clear that the only atomic operations are the ones defined in 32.5 [atomics] by the library. The intent is that no accesses are atomic unless the Standard describes them as such.

An additional problem is that, e.g., `new` and `delete` are defined to be synchronization operations, but they are not defined in Clauses 32.5 [atomics] and Clause 32 [thread].

Suggested resolution:

Change 6.10.2.2 [intro.races] paragraph 3 as follows:

> The library defines a number the set of atomic operations (32.5 [atomics]) and operations on mutexes (Clause 32 [thread]) that. Some of these, and some other library operations, such as those on mutexes ( Clause 32 [thread]) are specially identified as synchronization operations. These operations...

Notes from the April, 2017 teleconference:

CWG determined that this issue should be handled editorially; it will be in "review" status until the change has been made and verified. See editorial issue 1611.

Additional notes, October, 2018:

This is also library issue 2506. SG1 has requested a paper to deal with this issue, so it is no longer considered editorial.

#### 2298. Actions and expression evaluation

Section: 6.10.2.2 [intro.races] Status: open Submitter: Kazutoshi Satoda Date: 2016-01-21 Liaison: SG1

Section 6.10.2.2 [intro.races] uses the terms “action” and “expression evaluation” interchangeably. “Sequenced before” is defined on expression evaluations. Probably none of those is correct.

We should really be talking about individual accesses to “memory locations”. Talking about larger “expression evaluations” is incorrect, since they may include internal synchronization. Thus concurrent evaluation of large conflicting expression evaluations may not actually correspond to a data race. I'm not sure what term we should be using instead of “expression evaluation” to denote such individual accesses. Call it X for now.

There is also an issue with the fact that “sequenced before” is defined on expression evaluation. “Sequenced before” should also be defined on Xs. It doesn't make any sense to talk about “sequenced before” ordering on two evaluations when one includes the other. Whenever we say “`A` is sequenced before `B`”, we probably really mean that all Xs in `A` are sequenced before all Xs in `B`. We could probably just include a blanket statement to that effect.

Additional notes (April, 2022)

Forwarded to SG1 with paper issue 1234, reflecting the former "concurrency" status of this issue.

#### 3060. Change in behavior for `noexcept` `main`

Section: 6.10.3.1 [basic.start.main] Status: open Submitter: Jiang An Date: 2025-08-15

(From submission #752.)

Consider:

      int main() noexcept {}

Prior to P0012R1 (C++17), this was allowed, because `noexcept` was not part of a function type. This is now ill-formed, because such a `main` function is not of one of the types allowed in 6.10.3.1 [basic.start.main] paragraph 2.

This needs an Annex C entry or a relaxation of the rules. Note that the presence or absence of `noexcept` does not change behavior, because an exception leaving `main` calls `std::terminate` either way, and `main` cannot be named in expressions.

Suggested resolution:

Change in 6.10.3.1 [basic.start.main] paragraph 2 as follows:

> ... An implementation shall allow both
> - a "optionally `noexcept` function of () returning int" and
> - a "optionally `noexcept` function of (int, pointer to pointer to char) returning int"
> as the type of main (9.3.4.6 [dcl.fct]). ...

#### 371. Interleaving of constructor calls

Section: 6.10.3.2 [basic.start.static] Status: open Submitter: Matt Austern Date: 7 August 2002

Is a compiler allowed to interleave constructor calls when performing dynamic initialization of nonlocal objects? What I mean by interleaving is: beginning to execute a particular constructor, then going off and doing something else, then going back to the original constructor. I can't find anything explicit about this in 6.10.3.2 [basic.start.static].

I'll present a few different examples, some of which get a bit wild. But a lot of what this comes down to is exactly what the standard means when it talks about the order of initialization. If it says that some object x must be initialized before a particular event takes place, does that mean that x's constructor must be entered before that event, or does it mean that it must be exited before that event? If object x must be initialized before object y, does that mean that x's constructor must exit before y's constructor is entered?

(The answer to that question might just be common sense, but I couldn't find an answer in 6.10.3.2 [basic.start.static]. Actually, when I read 6.10.3.2 [basic.start.static] carefully, I find there are a lot of things I took for granted that aren't there.)

OK, so a few specific scenerios.

1. We have a translation unit with nonlocal objects A and B, both of which require dynamic initialization. A comes before B. A must be initialized before B. May the compiler start to construct A, get partway through the constructor, then construct B, and then go back to finishing A?
2. We have a translation unit with nonlocal object A and function f. Construction of A is deferred until after the first statement of main. A must be constructed before the first use of f. Is the compiler permitted to start constructing A, then execute f, then go back to constructing A?
3. We have nonlocal objects A and B, in two different translation units. The order in which A and B are constructed is unspecified by the Standard. Is the compiler permitted to begin constructing A, then construct B, then finish A's constructor? Note the implications of a 'yes' answer. If A's and B's constructor both call some function f, then the call stack might look like this:

          <runtime gunk>
            <Enter A's constructor>
               <Enter f>
                  <runtime gunk>
                     <Enter B's constructor>
                        <Enter f>
                        <Leave f>
                     <Leave B's constructor>
               <Leave f>
            <Leave A's constructor>

   The implication of a 'yes' answer for users is that any function called by a constructor, directly or indirectly, must be reentrant.
4. This last example is to show why a 'no' answer to #3 might be a problem too. New scenerio: we've got one translation unit containing a nonlocal object A and a function f1, and another translation unit containing a nonlocal object B and a function f2. A's constructor calls f2. Initialization of A and B is deferred until after the first statement of main(). Someone in main calls f1. Question: is the compiler permitted to start constructing A, then go off and construct B at some point before f2 gets called, then go back and finish constructing A? In fact, is the compiler required to do that? We've got an unpleasant tension here between the bad implications of a 'yes' answer to #3, and the explicit requirement in 6.10.3.2 [basic.start.static] paragraph 3.

At this point, you might be thinking we could avoid all of this nonsense by removing compilers' freedom to defer initialization until after the beginning of main(). I'd resist that, for two reasons. First, it would be a huge change to make after the standard has been out. Second, that freedom is necessary if we want to have support for dynamic libraries. I realize we don't yet say anything about dynamic libraries, but I'd hate to make decisions that would make such support even harder.

#### 1294. Side effects in dynamic/static initialization

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

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

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

> - the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization, and

> - the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.

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

CWG 2022-11-11

The precise normative identification of side effects relevant for the rule remains open. An approach similar to the constexpr model of considering the transitive hull of evaluations might be applicable.

#### 1659. Initialization order of thread_local template static data members

Section: 6.10.3.2 [basic.start.static] Status: open Submitter: Richard Smith Date: 2013-04-14

According to 6.10.3.2 [basic.start.static] paragraph 5,

> It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (6.3 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

This doesn't consider that initialization of instantiations of static data members of class templates (which can be `thread_local`) are unordered. Presumably odr-use of such a static data member should not trigger the initialization of any `thread_local` variable other than that one?

#### 2684. thread_local dynamic initialization

Section: 6.10.3.3 [basic.start.dynamic] Status: open Submitter: Jason Merrill Date: 2023-01-06

Subclause 6.10.3.3 [basic.start.dynamic] paragraph 7 specifies:

> It is implementation-defined whether the dynamic initialization of a non-block non-inline variable with thread storage duration is sequenced before the first statement of the initial function of a thread or is deferred. If it is deferred, the initialization associated with the entity for thread t is sequenced before the first non-initialization odr-use by t of any non-inline variable with thread storage duration defined in the same translation unit as the variable to be initialized. ...

How does the rule quoted above affect variables declared `constinit`? For example:

      extern thread_local constinit int x;

Clang and gcc do not emit suitable wrapper code to allow for deferred initialization of `x`, which is non-conforming. Should this be allowed?

Suggested resolution:

Change in 6.10.3.3 [basic.start.dynamic] paragraph 7 as follows:

> It is implementation-defined whether the dynamic initialization of a non-block non-inline variable with thread storage duration is sequenced before the first statement of the initial function of a thread or is deferred. If it is deferred, the initialization associated with the entity for thread t is sequenced before the first non-initialization odr-use by t of any non-inline variable with thread storage duration and dynamic initialization defined in the same translation unit as the variable to be initialized. ...

#### 2928. No ordering for initializing thread-local variables

Section: 6.10.3.3 [basic.start.dynamic] Status: open Submitter: CWG Date: 2024-08-16

The heading of subclause 6.10.3.3 [basic.start.dynamic] purports to specify "dynamic initialization of non-block variables", but the subclause actually specifies dynamic initialization of variables with static storage duration only, omitting variables with thread storage duration.

See also issue 2148.

#### 3042. Implicit object creation is insufficient to model effective type rule of C

Section: 7.2.1 [basic.lval] Status: open Submitter: Jiang An Date: 2025-07-04

(From submission #723.)

Consider:

      #include <stdlib.h>

      int main() {
       void* p = malloc(sizeof(int) + sizeof(float));

       *(int*)p = 0;        // #1
       *(float*)p = 0.0f;   // #2
      }

This example has well-defined behavior in C, because C23 6.5.1p6 says

> ... If a value is stored into an object having no declared type through an lvalue having a type that is not a non-atomic character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. ...

In contrast, implicit object creation for `malloc` happens once, and there is no single type that would make the program have defined behavior at both #1 and #2, since copy-assignment cannot create objects in C++.

Possible resolution:

Add to C.7.4 [diff.expr] as follows:

> Affected subclause: 7.6.19 [expr.assign]
> Change: Effective type rules are approximated with implicit object creation.
> Rationale: C++ has a stronger type system than C.
> Effect on original feature: Change to semantics of well-defined feature. Some well-defined C expressions will have undefined behavior in C++.
> [Example:

>       void f() {
>         void* p = malloc(sizeof(int) + sizeof(float));

>         *(int*)p = 0;
>         *(float*)p = 0.0f;   // well-formed in both C and C++; undefined behavior in C++
>       }

> --end example]
> Difficulty of converting: Programs must avoid implicit type changes of objects.
> How widely used: Seldom.

#### 2964. Reading "invalid pointer values"

Section: 7.3.2 [conv.lval] Status: open Submitter: Davis Herring Date: 2024-11-22

(From submission #645.)

Issue 2822 clarified that pointer values are not replaced by invalid pointer values when the duration of the storage of their pointee ends, but merely become invalid in the context of evaluations outside of the duration of the storage. However, 7.3.2 [conv.lval] bullet 3.3 still reads:

> The result of the conversion is determined according to the following rules:
> - ...
> - Otherwise, if the object to which the glvalue refers contains an invalid pointer value (6.8.6.5.3 [basic.stc.dynamic.deallocation]), the behavior is implementation-defined.
> - ...

Possible resolution:

Change in 7.3.2 [conv.lval] bullet 3.3 as follows:

> The result of the conversion is determined according to the following rules:
> - ...
> - Otherwise, if the object to which the glvalue refers contains an invalid pointer value (6.8.6.5.3 [basic.stc.dynamic.deallocation]) a pointer value that is not valid in the context of the conversion (6.9.4 [basic.compound]), the behavior is implementation-defined.
> - ...

#### 2438. Problems in the specification of qualification conversions

Section: 7.3.6 [conv.qual] Status: open Submitter: Richard Smith Date: 2019-08-14

1. A type has multiple cv-decompositions, and 7.3.6 [conv.qual] paragraph 3 does not say which one to use when determining the cv-combined type. Should this be the longest decomposition that works, i.e., the greatest n for which you can decompose both types? (We used to refer to the cv-qualification signature, which implicitly meant to take the longest decomposition.)

2. When computing the cv-combined types of two types `T1` and `T2`, if `U1` and `U2` are different, shouldn't we add `const` to all layers above that in the type?

3. cv03 is left unspecified by the wording in paragraph 3.

4. We are too eager to replace a Pi3 with “array of unknown bound of”. That should only happen if both Pi1 and Pi2 are array types, or we end up not forming a type T3 that is similar to T1. For example, the cv-combined type of `int**` and `const int (*)[]`, when decomposed with n == 2, is required to be `const int (*)[]` by the bulleted rules, and that type is not similar to the original `T1`.

5. In various places, we have operators that say, “if one operand is of pointer type, apply array-to-pointer conversions, pointer conversions, and qualification conversions to bring the two operands to their composite pointer type,” but that doesn't work, because the definition of composite pointer type can't cope with one operand being a pointer and the other being an array. We either need to define the composite pointer type of a pointer and an array (and, if that's done in terms of computing the cv-combined type, be careful to ensure that computing the cv-combined type actually works in that case) or to perform the array-to-pointer conversion before considering the composite pointer type.

#### 2981. Usual arithmetic conversions and result types

Section: 7.4 [expr.arith.conv] Status: open Submitter: Jan Schultke Date: 2025-01-02

(From submission #663.)

Subclause 7.4 [expr.arith.conv] paragraph 1 specifies:

> Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. ...

However, while the relational and equality operators do apply the usual arithmetic conversions, their result type is `bool` and not the common type from the usual arithmetic conversions.

Suggested resolution:

1. Change in 7.4 [expr.arith.conv] paragraph 1 as follows:

   > Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. ...

2. Change in 7.6.5 [expr.mul] paragraph 2 as follows:

   > The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type. The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on the operands to bring them to a common type, and determine the type of the result is a prvalue of that type.

3. Change in 7.6.6 [expr.add] paragraph 1 as follows:

   > The additive operators + and - group left-to-right. Each operand shall be a prvalue. The result is a prvalue. If both operands have arithmetic or unscoped enumeration type, the usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on the operands to bring them to a common type, and the result is of that type. Otherwise, if one operand has arithmetic or unscoped enumeration type, integral promotion is applied (7.3.7 [conv.prom]) to that operand. A converted or promoted operand is used in place of the corresponding original operand for the remainder of this section.

4. Change in 7.6.9 [expr.rel] paragraph 2 as follows:

   > The converted operands shall have arithmetic, enumeration, or pointer type. The operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) all yield prvalues of false or true. The type of the result is bool.

5. Change in 7.6.10 [expr.eq] paragraph 2 as follows:

   > The converted operands shall have scalar type. The operators == and != both yield true or false, i.e., a result prvalue of type bool. In each case below, the operands shall have the same type after the specified conversions have been applied.

6. Change in 7.6.11 [expr.bit.and] paragraph 1 as follows:

   > The & operator groups left-to-right. The operands shall be of integral or unscoped enumeration type. The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on the operands to bring them to a common type, and the result is a prvalue of that type. Given the coefficients xi and yi of the base-2 representation (6.9.2 [basic.fundamental]) of the converted operands x and y, the coefficient ri of the base-2 representation of the result r is 1 if both xi and yi are 1, and 0 otherwise.

7. Change in 7.6.12 [expr.xor] paragraph 1 as follows:

   > The ^ operator groups left-to-right. The operands shall be of integral or unscoped enumeration type. The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on the operands to bring them to a common type, and the result is a prvalue of that type. Given the coefficients xi and yi of the base-2 representation (6.9.2 [basic.fundamental]) of the converted operands x and y, the coefficient ri of the base-2 representation of the result r is 1 if either (but not both) of xi and yi is 1, and 0 otherwise.

8. Change in 7.6.13 [expr.or] paragraph 1 as follows:

   > The | operator groups left-to-right. The operands shall be of integral or unscoped enumeration type. The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on the operands to bring them to a common type, and the result is a prvalue of that type. Given the coefficients xi and yi of the base-2 representation (6.9.2 [basic.fundamental]) of the converted operands x and y, the coefficient ri of the base-2 representation of the result r is 1 if at least one of xi and yi is 1, and 0 otherwise.

#### 2989. Remove misleading general allowance for parentheses

Section: 7.5.4 [expr.prim.paren] Status: open Submitter: Brian Bi Date: 2025-01-17

(From submission #661.)

The second sentence in 7.5.4 [expr.prim.paren] paragraph 1 occasionally causes confusion whether parenthesized constructs are allowed where the grammar or other rules do not specifically allow them.

Specific areas of concern are:

- Parenthesized integer literal with value 0 as a null pointer constant, e.g. `(((0ULL)))` (supported by all implementations)
- Parenthesized operand for `&` when forming a pointer-to-member (7.6.2.2 [expr.unary.op] paragraph 3)
- Parenthesized non-type template arguments when matching template specializations (13.7.6.1 [temp.spec.partial.general] paragraph 8, 13.10.3.6 [temp.deduct.type] paragraph 8)

Possible resolution:

1. Change in 6.8.5 [basic.indet] bullet 2.1 as follows:

   > - If an indeterminate or erroneous value of unsigned ordinary character type (6.9.2 [basic.fundamental]) or std::byte type (17.2.1 [cstddef.syn]) is produced by the evaluation of:
   >   - the operand of a parenthesized expression (7.5.4 [expr.prim.paren]),
   >   - the second or third operand of a conditional expression (7.6.16 [expr.cond]),
   >   - the right operand of a comma expression (7.6.20 [expr.comma]),
   >   - ...
   >   then the result of the operation is an indeterminate value or that erroneous value, respectively.
   > - ...

2. Change in 6.9.2 [basic.fundamental] paragraph 15 as follows:

   > A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. An expression of type cv void shall be used only as
   > - an expression statement (8.3 [stmt.expr]),
   > - the expression in a return statement (8.8.4 [stmt.return]) for a function with the return type cv void,
   > - the operand of a parenthesized expression (7.5.4 [expr.prim.paren]),
   > - an operand of a comma expression (7.6.20 [expr.comma]),
   > - the second or third operand of ?: a conditional expression (7.6.16 [expr.cond]),
   > - the operand of a typeid expression (7.6.1.8 [expr.typeid]),
   > - ...

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

   > A null pointer constant is an a (possibly parenthesized) integer literal (5.13.2 [lex.icon]) with value zero or a prvalue of type std::nullptr_t. ...

4. Change 7.5.4 [expr.prim.paren] paragraph 1 as follows:

   > A parenthesized expression (E) is a primary expression whose type, result, and value category are identical to those of E. The parenthesized expression is a bit-field if E is a bit-field. If (E) is a discarded-value expression (7.2.3 [expr.context]), so is E. The parenthesized expression can be used in exactly the same contexts as those where E can be used, and with the same meaning, except as otherwise indicated.

5. Change in 7.5.5.5 [expr.prim.id.dtor] paragraph 2 as follows:

   > 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 (possibly parenthesized) postfix-expression of a function call (7.6.1.3 [expr.call]).

6. Change in 11.5.1 [class.union.general] paragraph 5 as follows:

   > When the left operand of an assignment operator involves a member access expression (7.6.1.5 [expr.ref]) that nominates a union member, it may begin the lifetime of that union member, as described below. For an expression E, define the set S(E) of subexpressions of E as follows:
   > - If E is of the form (A), S(E) is S(A).
   > - If E is of the form A.B, S(E) contains the elements of S(A), and also contains A.B if B names a union member of a non-class, non-array type, or of a class type with a trivial default constructor that is not deleted, or an array of such types.
   > - If E is of the form A[B] and is interpreted as a built-in array subscripting operator, S(E) is S(A) if A is of array type, S(B) if B is of array type, and empty otherwise.
   > - Otherwise, S(E) is empty.

7. Change in 11.9.6 [class.copy.elision] paragraph 1 as follows:

   > This elision of object creation, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
   > - in a return statement (8.8.4 [stmt.return]) in a function with a class return type, when the expression is the (possibly parenthesized) name of a non-volatile object o with automatic storage duration (other than a function parameter or a variable introduced by the exception-declaration of a handler (14.4 [except.handle])), the copy-initialization of the result object can be omitted by constructing o directly into the function call's result object;
   > - in a throw-expression (7.6.18 [expr.throw]), when the operand is the (possibly parenthesized) name of a non-volatile object o with automatic storage duration (other than a function parameter or a variable introduced by the exception-declaration of a handler ) that belongs to a scope that does not contain the innermost enclosing compound-statement associated with a try-block (if there is one), the copy-initialization of the exception object can be omitted by constructing o directly into the exception object;
   > - in a coroutine (9.6.4 [dcl.fct.def.coroutine]), ...;
   > - when the exception-declaration of a handler (14.4 [except.handle]) declares an object o, ...

8. Change in 12.2.2.2.1 [over.match.call.general] paragraph 1 as follows:

   > In a function call (7.6.1.3 [expr.call])

   >        postfix-expression ( expression-listopt )

   > if the postfix-expression names at least one function or function template is an overload set, overload resolution is applied as specified in 12.2.2.2.2 [over.call.func]. If the postfix-expression denotes an object of class type, overload resolution is applied as specified in 12.2.2.2.3 [over.call.object].

9. Change in 12.2.2.2.2 [over.call.func] paragraph 1 as follows:

   > Of interest in 12.2.2.2.2 [over.call.func] are only those function calls in which the postfix-expression ultimately contains an id-expression that denotes one or more functions is an overload set. Such a postfix-expression, perhaps nested arbitrarily deep in parentheses, has one of the following forms: ...

10. Change in 12.3 [over.over] paragraph 1 as follows:

    > An id-expression whose expression E designates the address of an overload set S if E is
    > - an id-expression, or
    > - of the form `& X`, where X is a (possibly parenthesized) id-expression, or
    > - of the form `( X )`, where X is one of these expressions,
    > where the terminal name of the id-expression refers to an overload set S and that E appears without arguments. Such an expression is resolved to a function, a pointer to function, or a pointer to member function for a specific function that is chosen from a set of functions selected from S determined based on the target type required in the context (if any), as described below. The target can be .... If the target type contains a placeholder type, placeholder type deduction is performed (9.2.9.7.2 [dcl.type.auto.deduct]), and the remainder of this subclause uses the target type so deduced. The id-expression can be preceded by the & operator. [Note 1: Any redundant set of parentheses surrounding the function name is ignored (7.5.4 [expr.prim.paren]). —end note]

11. Change in 12.3 [over.over] paragraph 6 as follows:

          int f(double);
          int f(int);
          int (*pfd)(double) = &f;         // selects f(double)
          int (*pfi)(int) = (((&f)));      // selects f(int)
          ...

12. Change in 13.7.6.1 [temp.spec.partial.general] paragraph 8 as follows:

    > A non-type argument is non-specialized if it is the unparenthesized name of a non-type parameter. All other non-type arguments are specialized.

13. Change in 13.10.3.6 [temp.deduct.type] paragraph 8 as follows:

    > ... [Note 3: If a type matches such a form but contains no Ts, is, or TTs, deduction is not possible. —end note] Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i. [ Note: If a non-type template parameter is parenthesized, deduction is not possible. [ Example:

    >       template<int N> struct X {};
    >       template<int N> void f(X<(N)>);   // #1
    >       void g() { f(X<0>()); }           // error: cannot deduce N in #1

    > -- end example ] -- end note ]

#### 2972. Declarative nested-name-specifier naming a partial specialization

Section: 7.5.5.3 [expr.prim.id.qual] Status: open Submitter: Hubert Tong Date: 2024-12-20

Consider:

      template <auto AX> struct A;
      template <int AX> struct A<AX> {
       static constexpr int X = AX;
      };
      template <int AX> const int A<AX>::X;   // #1

According to 7.5.5.3 [expr.prim.id.qual] paragraph 3, the declarative nested-name-specifier at #1 names the primary template and is thus ill-formed:

> ... If a nested-name-specifier N is declarative and has a simple-template-id with a template argument list A that involves a template parameter, let T be the template nominated by N without A. T shall be a class template.
> - If A is the template argument list (13.4 [temp.arg]) of the corresponding template-head H (13.7.3 [temp.mem]), N nominates the primary template of T ; H shall be equivalent to the template-head of T (13.7.7.2 [temp.over.link]).
> - Otherwise, N nominates the partial specialization (13.7.6 [temp.spec.partial]) of T whose template argument list is equivalent to A (13.7.7.2 [temp.over.link]); the program is ill-formed if no such partial specialization exists.

However, the qualified-id is obviously intended to refer a member of the partial specialization.

Furthermore, the specification does not handle references to constrained partial specializations:

      template <typename T> struct A;
      template <typename T> concept C = true;

      template <typename T>
      requires C<T>
      struct A<T *> {
        struct B;
      };

      template <typename T>
      requires true && C<T>
      struct A<T *> {
        struct B;                   // #1
      };

      template <typename T>
      requires true && C<T>
      struct A<T *>::B {};         // ought to refer to #1

#### 2473. Parentheses in pseudo-destructor calls

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

According to 7.5.5.5 [expr.prim.id.dtor] 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:

      typedef int T;
      void f(int* p) {
        (p->~T)();   // Ill-formed?
      }

Presumably this is an oversight.

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

Section: 7.5.8.1 [expr.prim.req.general] Status: open Submitter: Barry Revzin Date: 2022-04-07 Liaison: EWG

Consider:

      template <typename T>
      concept C = requires (typename T::type x) {
        x + 1;
      };

      static_assert(!C<int>);

All implementations accept this translation unit. However, the rule in 7.5.8.1 [expr.prim.req.general] 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 `false`; 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 `true`.

Proposed resolution (approved by CWG 2023-06-17):

Change in 7.5.8.1 [expr.prim.req.general] 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, ...

CWG 2023-11-09

The initial example is not the point of the issue, but the very similar following case:

      template <typename T>
      constexpr bool b = requires (T::type x) { x + 1; };

      static_assert(!b<int>);  // de-jure ill-formed, but widely accepted by implementations

Users were vocal they wanted this not to be an error, but just a substitution failure. Reportedly, the original design intent was that this situation is a hard error, though.

Soliciting guidance from EWG how to handle this issue via paper issue 1695.

EWG 2024-03-18

EWG invites a paper to propose a change.

#### 3056. Missing semicolons in grammar for type-requirement

Section: 7.5.8.3 [expr.prim.req.type] Status: open Submitter: Peter Bindels Date: 2025-08-14

(From submission #751.)

These lines in the example in 7.5.8.3 [expr.prim.req.type] paragraph 1 are not covered by the grammar:

      typename [:T::r1:];        // fails if T::r1 is not a reflection of a type
      typename [:T::r2:]<int>;   // fails if T::r2 is not a reflection of a template Z for which Z<int> is a type

Suggested resolution:

Change in 7.5.8.3 [expr.prim.req.type] paragraph 1 as follows:

>       type-requirement:
>         typename nested-name-specifieropt type-name ;
>         typename splice-specifier ;
>         typename splice-specialization-specifier ;

#### 2739. Nested requirement not a constant expression

Section: 7.5.8.5 [expr.prim.req.nested] Status: open Submitter: Barry Revzin Date: 2022-09-20

Consider:

      struct NonConstant { static bool f() { return true; } };
      struct True { static constexpr bool f() { return true; } };
      struct False { static constexpr bool f() { return false; } };

      template <class T>
      concept C = requires { requires T::f(); };

      static_assert(!C<NonConstant>);   // #1
      static_assert(C<True>);
      static_assert(!C<False>);

clang accepts; gcc, MSVC, and EDG all consider #1 to be ill-formed. Subclause 7.5.8.5 [expr.prim.req.nested] paragraph 1 specifies:

> The constraint-expression shall be satisfied (13.5.3 [temp.constr.decl]) by the substituted template arguments, if any. Substitution of template arguments into a nested-requirement does not result in substitution into the constraint-expression other than as specified in 13.5.2 [temp.constr.constr].

Neither `C<NonConstant>` nor `C<False>` are saisfied, but it is unclear whether those two cases should be treated differently. Subclause 13.5.2.3 [temp.constr.atomic] paragraph 3 might be relevant:

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

An example from the standard library is:

      template<class G>
       concept uniform_random_bit_generator =
         invocable<G&> && unsigned_integral<invoke_result_t<G&>> &&
         requires {
           { G::min() } -> same_as<invoke_result_t<G&>>;
           { G::max() } -> same_as<invoke_result_t<G&>>;
           requires bool_constant<(G::min() < G::max())>::value;
        };

Is the `bool_constant` part necessary to ensure that `G::min() < G::max()` is a constant expression?

#### 2284. Sequencing of braced-init-list arguments

Section: 7.6.1.3 [expr.call] Status: open Submitter: Richard Smith Date: 2016-06-30

As of P0400R0 (Wording for Order of Evaluation of Function Arguments), we have in subclause 7.6.1.3 [expr.call] paragraph 8:

> The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

What about the case when the element in the expression-list is a braced-init-list rather than an expression? The braced-init-list is certainly evaluated left-to-right, but is that required to happen after we evaluate the postfix-expression?

#### 2515. Result of a function call

Section: 7.6.1.3 [expr.call] Status: open Submitter: Andrey Erokhin Date: 2021-11-06

The editorial change referred to in the resolution of issue 2495 updated the terminology used to describe the `return` statement to allow for the fact that the operand could be a braced-init-list instead of an expression. A similar problem exists describing the result of a function call in 7.6.1.3 [expr.call] paragraph 9:

> The result of a function call is the result of the possibly-converted operand of the `return` statement (8.8.4 [stmt.return]) that transferred control out of the called function

It's incorrect to refer to “converting” the operand when it is a braced-init-list.

#### 2660. Confusing term "this parameter"

Section: 7.6.1.3 [expr.call] Status: open Submitter: Anoop Rana Date: 2022-11-23

Subclause 7.6.1.3 [expr.call] paragraph 7 specifies:

> If the function is an implicit object member function, the `this` parameter of the function (7.5.3 [expr.prim.this]) is initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]).

The term "`this` parameter" is undefined.

Suggested resolution:

Change in 7.6.1.3 [expr.call] paragraph 7 as follows:

> If the function is an implicit object member function, the `this` implicit object parameter of the function (7.5.3 [expr.prim.this]) is initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]).

#### 2688. Calling explicit object member functions

Section: 7.6.1.3 [expr.call] Status: open Submitter: Matthew House Date: 2023-01-16

Subclause 7.6.1.3 [expr.call] paragraphs 1 and 2 do not properly handle explicit object member functions. Such a function can be called via a function pointer, via an lvalue obtained by dereferencing a function pointer, and via a class member access expression.

#### 3054. Use of default arguments depending on shape of postfix-expression in a function call

Section: 7.6.1.3 [expr.call] Status: open Submitter: Benjamin Sch. Date: 2025-08-09

(From submission #744.)

Consider:

      void f(int = 0) {}

      void g() {
        f();          // accepted by all
        (&f)();       // accepted by EDG and clang, rejected by GCC and MSVC
        (*f)();       // accepted by EDG and MSVC, rejected by GCC and clang
        (0, f)();     // accepted by EDG, rejected by MSVC, GCC and clang
        static_cast<void(&)(int)>(f)();  // accepted by MSVC, rejected by EDG, GCC, and clang
        static_cast<void(*)(int)>(f)();  // rejected by all

        auto p = f;
        p();          // rejected by all
      }

It seems reasonably clear from 12.2.2.2.1 [over.match.call.general] that only the first two cases involve overload resolution for a function call; the rest does not. Default arguments are only considered when overload resolution for a function call selected the function; 7.6.1.3 [expr.call] paragraph 6 should be clarified.

Possible resolution:

See also core issue 2989.

1. Change in 7.6.1.3 [expr.call] paragraph 6 as follows:

   > When a function is called, each parameter (9.3.4.6 [dcl.fct]) is initialized (9.5 [dcl.init], 11.4.5.3 [class.copy.ctor]) with its corresponding argument, and each precondition assertion of the function is evaluated (9.4.1 [dcl.contract.func]). If the function is an explicit object member function and there is an implied object argument (12.2.2.2.2 [over.call.func]), the list of provided arguments is preceded by the implied object argument for the purposes of this correspondence. If the function to be called is selected by overload resolution (12.2.2.2.1 [over.match.call.general]) and there is no corresponding argument, the default argument for the parameter is used. ...

2. Change in 12.2.2.2.1 [over.match.call.general] paragraph 2 as follows:

   > If the postfix-expression is of the (possibly parenthesized) form `& primary-expression`, where the primary-expression is a possibly-parenthesized id-expression or splice-expression and is the address of an overload set, overload resolution is applied using that set as described above. ...

#### 914. Value-initialization of array types

Section: 7.6.1.4 [expr.type.conv] Status: open Submitter: Gabriel Dos Reis Date: 10 June, 2009 Liaison: EWG

Although value-initialization is defined for array types and the `()` initializer is permitted in a mem-initializer naming an array member of a class, the syntax `T()` (where `` is an array type) is explicitly forbidden by 7.6.1.4 [expr.type.conv] paragraph 2. This is inconsistent and the syntax should be permitted.

Rationale (July, 2009):

The CWG was not convinced of the utility of this extension, especially in light of questions about handling the lifetime of temporary arrays. This suggestion needs a proposal and analysis by the EWG before it can be considered by the CWG.

EWG 2022-11-11

This is a defect; a paper is needed. This is tracked in github issue cplusplus/papers#1372.

#### 2705. Accessing ambiguous subobjects

Section: 7.6.1.5 [expr.ref] Status: open Submitter: Davis Herring Date: 2023-02-11

Consider:

      struct A {int i;};
      struct X : A {};
      struct Y : A {};
      struct S : X,Y {};

      void f(S &s) {++s.X::i;}

There is a rule that `&s` be able to "be implicitly converted to a pointer to the naming class of the right operand" (11.8.3 [class.access.base] paragraph 6), which correctly selects `X` here, but 7.6.1.5 [expr.ref] bullet 6.2 merely says that

> If E2 is a non-static data member [...], the expression designates the corresponding member subobject of the object designated by the first expression.

`E2` (i.e. `X::i`) and `Y::i` equally denote `A::i` (since name lookup produces declarations), but there is no rule that indicates which `A::i` is selected as the "corresponding member subobject". (Note that `s.X::A::i` means the same as `s.A::i` and is ambiguous.)

Suggested resolution:

Change 11.8.3 [class.access.base] paragraph 6 to actually perform the conversion it requires, possibly moving it to 7.6.1.5 [expr.ref] since it is not about access control; the definition of "naming class" in 11.8.3 [class.access.base] paragraph 5 would then need to be italicized.

#### 2957. Evaluating a reference member should constitute access

Section: 7.6.1.5 [expr.ref] Status: open Submitter: Jiang An Date: 2024-11-06

(From submissions #634 and #637.)

The standard permits that references do not occupy storage. The term "access" (3.1 [defns.access]) is not applicable to references. Yet, references can participate in data races (6.10.1 [intro.execution] paragraph 10, 6.10.2.2 [intro.races] paragraph 2). For example:

      int x, y;
      struct S {
        int &r;
      } s{x};

      void thread_1() {
        new (&s) S{y};   // transparent replacement
      }

      void thread_2() {
        auto& r = s.r; // OK ?! Refers to x or y?
      }

Also consider the impact on constant evaluation:

      int n{};
      struct S { int& r; };
      constexpr S s{n};
      constexpr volatile S s2{n};

      static_assert(&static_cast<const volatile S&>(s).r == &n); // GCC rejects
      static_assert(&s2.r == &n); // GCC and Clang reject

#### 2959. Naming enumerators in class member access expressions

Section: 7.6.1.5 [expr.ref] Status: open Submitter: keinflue Date: 2024-11-04

While lookup for a member enumerator is supported using class member access syntax (7.6.1.5 [expr.ref] bullet 7.5, the following example from 9.8.2 [enum.udecl] paragraph 2 is ill-formed:

      enum class fruit { orange, apple };
      struct S {
       using enum fruit;       // OK, introduces orange and apple into S
      };
      void f() {
       S s;
       s.orange;           // OK, names fruit::orange
       S::orange;          // OK, names fruit::orange
      }

Additional examples to consider:

      enum class fruit { orange };
      struct S { using fruit::orange; } s;
      auto a = s.S::orange; // OK?
      auto b = s.fruit::orange; // OK?
      struct T { using fruit::orange; } t;
      auto c = s.T::orange; // OK?

"Using enum" was introduced by paper P1099R5.

Issue 2557 makes the original example well-formed, but none of the qualified additional examples.

#### 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:

        struct S {
            signed long long sll: 3;
        };
        S s = { -1 };

the expressions `s.sll-- < 0u` and `s.sll < 0u` have different results. The reason for this is that `s.sll--` is an rvalue of type `signed long long` (7.6.1.6 [expr.post.incr]), which means that the usual arithmetic conversions (Clause 7 [expr] paragraph 10) convert `0u` to `signed long long` and the result is `true`. `s.sll`, on the other hand, is a bit-field lvalue, which is promoted (7.3.7 [conv.prom] paragraph 3) to `int`; both operands of `<` have the same rank, so `s.sll` is converted to `unsigned int` to match the type of `0u` and the result is `false`. This disparity seems undesirable.

#### 282. Namespace for `extended_type_info`

Section: 7.6.1.8 [expr.typeid] Status: open Submitter: Jens Maurer Date: 01 May 2001

The original proposed resolution for issue 160 included changing `extended_type_info` (7.6.1.8 [expr.typeid] paragraph 1, footnote 61) to `std::extended_type_info`. There was no consensus on whether this name ought to be part of namespace `std` or in a vendor-specific namespace, so the question was moved into a separate issue.

#### 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 [expr.static.cast] 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 void *p;
       int *q = (int*)p;

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 [expr.static.cast] paragraph 7 allows `static_cast` to perform the inverse of a standard conversion sequence, the C-style cast is interpreted as just a `static_cast` without a `const_cast` and is thus ill-formed.

#### 3026. Class for pointer-to-member formation

Section: 7.6.2.2 [expr.unary.op] Status: open Submitter: Hubert Tong Date: 2025-05-11

(From submission #705.)

Consider:

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

      template <typename> struct Q;
      template <> struct Q<A> {};
      template <typename T> Q<T> f(int T::*);

      Q<A> g() { return f(&B::x); }  // OK
      Q<A> h() { return f(&B::y); }  // OK

For both cases, the pointer to member is of type "pointer to member of class `A`", but the wording is not clear.

Suggested resolution:

Change in 7.6.2.2 [expr.unary.op] bullet 3.1 as follows:

> The operand of the unary & operator shall be an lvalue of some type T.
> - If the operand is a qualified-id naming a non-static or variant class member `m` of some class C, other than an explicit object member function, then let `C` be the class, that is not an anonymous union, of which `m` is a direct member. the The result has type “pointer to member of class C of type T” and designates C::m. [Note: A qualified-id that names a member of a namespace-scope anonymous union is considered to be a class member access expression (7.5.5.1 [expr.prim.id.general]) and thus cannot be used to form a pointer to member.-- end note]
> - ...

#### 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 [expr.sizeof] 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: ... ]

#### 2817. sizeof(abstract class) is underspecified

Section: 7.6.2.5 [expr.sizeof] Status: open Submitter: Jiang An Date: 2023-02-23

Subclause 7.6.2.5 [expr.sizeof] paragraph 1 specifies:

> The sizeof operator yields the number of bytes occupied by a non-potentially-overlapping object of the type of its operand. The operand is either an expression, which is an unevaluated operand (7.2.3 [expr.context]), or a parenthesized type-id. ...

Since an abstract class can be used only in a situation where it is a potentially-overlapping object, it is unclear what its size is.

#### 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 [expr.new] paragraph 10:

> For arrays of `char` and `unsigned char`, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the most stringent alignment requirement (6.9 [basic.types]) of any object type whose size is no greater than the size of the array being created.

The intent of this wording is that the pointer returned by the new-expression will be suitably aligned for any data type that might be placed into the allocated storage (since the allocation function is constrained to return a pointer to maximally-aligned storage). However, there is an implicit assumption that each alignment requirement is an integral multiple of all smaller alignment requirements. While this is probably a valid assumption for all real architectures, there's no reason that the Standard should require it.

For example, assume that `int` has an alignment requirement of 3 bytes and `double` has an alignment requirement of 4 bytes. The current wording only requires that a buffer that is big enough for an `int` or a `double` be aligned on a 4-byte boundary (the more stringent requirement), but that would allow the buffer to be allocated on an 8-byte boundary — which might not be an acceptable location for an `int`.

Suggested resolution: Change "of any object type" to "of every object type."

A similar assumption can be found in 7.6.1.10 [expr.reinterpret.cast] paragraph 7:

> ...converting an rvalue of type "pointer to `T1`" to the type "pointer to `T2`" (where ... the alignment requirements of `T2` are no stricter than those of `T1`) and back to its original type yields the original pointer value...

Suggested resolution: Change the wording to

> ...converting an rvalue of type "pointer to `T1`" to the type "pointer to `T2`" (where ... the alignment requirements of `T1` are an integer multiple of those of `T2`) and back to its original type yields the original pointer value...

The same change would also be needed in paragraph 9.

Additional note (June, 2022):

Subclause 6.8.3 [basic.align] 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.

#### 901. Deleted `operator delete`

Section: 7.6.2.8 [expr.new] Status: open 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.

#### 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.8.6.5.3 [basic.stc.dynamic.deallocation] paragraph 2 indicates that a deallocation function can be an instance of a function template.

One possibility for this processing might be to perform template argument deduction on any deallocation function templates; if there is a specialization that matches the allocation function, by the criteria listed in paragraph 20, that function template would be instantiated and used, although a matching non-template function would take precedence as is the usual outcome of overloading between function template specializations and non-template functions.

Another possibility might be to match non-template deallocation functions with non-template allocation functions and template deallocation functions with template allocation functions.

There is a slightly related wording problem in 7.6.2.8 [expr.new] paragraph 21:

> If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax.

This wording ignores the possibility of default arguments in the allocation function, in which case the arguments passed to the deallocation function might be a superset of those specified in the new-placement.

(See also issue 1682.)

#### 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.9.4 [basic.compound] 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 [expr.new] 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?

>     T *p = new T[0];
>     T *q = new T;

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.8.6.5.3 [basic.stc.dynamic.deallocation] 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.8.6.5.2 [basic.stc.dynamic.allocation] 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".

#### 2812. Allocation with explicit alignment

Section: 7.6.2.8 [expr.new] Status: open Submitter: Tim Song Date: 2023-10-18

Consider:

      #include <new>
      auto p = new (std::align_val_t{64}) T;

Is this example well-formed for `T = int`? This invokes the allocation function

      ::operator new(size_t, align_val_t);

If this is considered a "placement allocation function", the code is ill-formed per 7.6.2.8 [expr.new] paragraph 28, because deallocation function lookup finds a usual deallocation function. If this is considered a "usual allocation function", the rules find `::operator delete(void*)` as the corresponding deallocation function.

There is implementation divergence: MSVC rejects; clang and gcc accept, but invoke `::operator delete(void*, align_val_t)` if the constructor exits via an exception.

If `T` is a type with new-extended alignment, the preferred allocation function is `operator new(std::size_t, std::align_val_t, std::align_val_t)` or, as a fallback, `operator new(std::size_t, std::align_val_t)`, removing the alignment information of `T` (7.6.2.8 [expr.new] paragraph 19).

As another concern, subclause 17.6.3.2 [new.delete.single] paragraph 11 disallows mixing aligned and unaligned allocation/deallocation functions, and implementations rely on that precondition. Thus, it is impossible to deallocate the memory obtained in the example using a delete-expression; an explicit deallocation function call appears to be required.

Possible resolution:

Disallow invocation of one of the allocation functions specified in 17.6.3.2 [new.delete.single] and 17.6.3.3 [new.delete.array] if the new-placement syntax is used with anything but a single expression of type `std::nothrow_t`.

Alternatively, make this conditionally-supported to offer a richer feature set on platforms where mixing aligned and unaligned allocation/deallocation works.

#### 2912. Too-large value for size in array new

Section: 7.6.2.8 [expr.new] Status: open Submitter: Mital Ashok Date: 2024-06-20

(From submission #560.)

Consider an implementation with a `size_t` smaller than `long long`. An implementation should flag too-large values for the size in an array "new", in the same manner it flags negative values. For example:

      int *p = new int[ULLONG_MAX];

Possible resolution:

Change in 7.6.2.8 [expr.new] paragraph 8 as follows:

> If the expression in a noptr-new-declarator is present, it is implicitly converted to std::size_t. The value of the expression is invalid if:
> - the expression is of non-class type and its value before converting to std::size_t is less than zero or greater than the maximum value representable as a `std::size_t`;
> - the expression is of class type and its value before application of the second standard conversion (12.2.4.2.3 [over.ics.user]) [ Footnote: ... ] is less than zero or greater than the maximum value representable as a `std::size_t`;
> - its value is such that the size of the allocated object would exceed the implementation-defined limit (Annex B); or
> - the new-initializer is a braced-init-list and the number of array elements for which initializers are provided (including the terminating '\0' in a string-literal (5.13.5 [lex.string])) exceeds the number of elements to initialize.

#### 3011. Parenthesized aggregate initialization for new-expressions

Section: 7.6.2.8 [expr.new] Status: open Submitter: Benjamin Sch. Date: 2025-03-17

(From submission #685.)

Subclause 7.6.2.8 [expr.new] paragraph 8 and paragraph 9 contain special cases involving aggregate initialization using braced-init-lists, which need to be amended for parenthesized aggregate initialization. The corresponding rules for the initialization of variables are in 9.5.1 [dcl.init.general] bullet 16.5 for arrays and in 9.5.1 [dcl.init.general] bullet 16.6.2.2 for aggregate classes.

Possible resolution:

1. Change in 7.6.2.8 [expr.new] bullet 8.4 as follows:

   > If the expression in a noptr-new-declarator is present, it is implicitly converted to std::size_t. The value of the expression is invalid if
   > - ...
   > - the new-initializer is a braced-init-list present and the number of array elements for which initializers are provided (including the terminating '\0' in a string-literal (5.13.5 [lex.string])) in a braced-init-list or parenthesized expression-list exceeds the number of elements to initialize.

2. Change in 7.6.2.8 [expr.new] paragraph 9 as follows:

   > If the allocated type is an array, the new-initializer is a braced-init-list is present and is not `()`, 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 as follows:
   > - If the new-initializer is a braced-init-list, the hypothetical element is copy-initialized from an empty initializer list (9.5.5 [dcl.init.list]).
   > - If the new-initializer is a parenthesized expression-list, the hypothetical element is value-initialized (9.5.1 [dcl.init.general]).
   > [Note 5: The array can contain more elements than there are elements in the braced-init-list new-initializer, requiring initialization of the remainder of the array elements from an empty initializer list as appropriate. —end note]

#### 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 [expr.new] paragraph 10 says that the result of an array allocation function and the value of the array new-expression from which it was invoked may be different, allowing for space preceding the array to be used for implementation purposes such as saving the number of elements in the array. However, there is no corresponding description of the relationship between the operand of an array delete-expression and the argument passed to its deallocation function.

6.8.6.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 [expr.delete] paragraph 2 requires that the operand of an array delete-expression "be the pointer value which resulted from a previous array new-expression.") However, it is not completely clear whether the "shall" expresses an implementation requirement or a program requirement (or both). Furthermore, there is no direct statement about user-defined deallocation functions.

Suggested resolution: A note should be added to 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.

#### 2805. Underspecified selection of deallocation function

Section: 7.6.2.9 [expr.delete] Status: open Submitter: Lénárd Szolnoki Date: 2023-10-12

Consider:

      #include <memory>

      struct B {
        void operator delete(B* ptr, std::destroying_delete_t);
      };

      struct D : B {
        void operator delete(D* ptr, std::destroying_delete_t);
        using B::operator delete;
      };

      void foo(D* ptr) {
        delete ptr;
      }

The selection rules in 7.6.2.9 [expr.delete] paragraph 10 do not disambiguate this case. There is implementation divergence.

A similar situation arises for the following example:

      struct A {
        void operator delete(void *);
      };

      struct B {
        void operator delete(void *);
      };

      struct C : A, B {
        using A::operator delete;
        using B::operator delete;
      };

      void foo(C* ptr) {
        delete ptr;
      }

CWG 2023-10-20

The ordered list of preferences should be retained. Eventual disambiguation should be via overload resolution, but it is unclear how to deal with the unspecified choice between overloads with and without a `std::size_t` parameter.

#### 2889. Requiring an accessible destructor for destroying operator delete

Section: 7.6.2.9 [expr.delete] Status: open Submitter: Lauri Vasama Date: 2024-05-03

(From submission #532.)

Subclause 7.6.2.9 [expr.delete] paragraph 6 specifies that the destructor must be accessible even if a destroying operator delete is used:

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

This does not seem to be intended.

Possible resolution:

Change in 7.6.2.9 [expr.delete] paragraph 6 as follows:

> If the value type of the operand of the delete-expression is not a null pointer value is a pointer to a class type or (possibly multidimensional) array thereof, and the selected deallocation function (see below) is not a destroying operator delete: , evaluating the delete-expression 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 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]).
> - The destructor is potentially invoked (11.4.7 [class.dtor]).
> - If the pointer value is not a null pointer value, evaluating a single-object delete expression destroys the object pointed to by the operand, and evaluating an array delete expression destroys each element of the array pointed to by the operand in order of decreasing index.

CWG 2024-05-31

This area of the wording is affected by P3144 (Deprecate Delete of a Pointer to an Incomplete Type). The wording above requires a class definition due to the "potentially invoked" phrasing. See also issue 2880.

#### 2878. C-style casts to reference types

Section: 7.6.3 [expr.cast] Status: open Submitter: Hubert Tong Date: 2024-03-20 Liaison: EWG

(From submission #522.)

Consider:

      struct A {};
      struct B : A {};
      const B b;
      void f() {
        (A &&)b;          // #1
        const_cast<A &&>(static_cast<const A &>(b));
        const_cast<A &&>(static_cast<const volatile A &&>(b));
        const_cast<A &&>(static_cast<A>(b));  // slicing interpretation
      }

Is #1 ill-formed because of the three alternative valid interpretations shown in the following lines?

Also consider:

      struct B { };
      const B f();
      B& b1 = const_cast<B&>(static_cast<const B&>(f())); // OK
      B& b2 = (B&)f(); // ???

There is implementation divergence: gcc and MSVC accept, clang and EDG accept `b1` and reject `b2`. If `f` is changed to return non-const, gcc also rejects `b2`.

For another example:

      struct A {
        operator const B() = delete;
      } a;

      B& b3 = const_cast<B&>(static_cast<const B&>(a)); // error, deleted
      B& b4 = (B&)a; // error or reinterpret_cast?

Implementations agree that `b3` is ill-formed for selecting the deleted conversion operator function, but `b4` is considered a `reinterpret_cast` by the majority of implementations.

CWG 2024-05-17

Casting away rvalueness in example `b2` is surprising to perform in a C-style cast. The `b4` example is also interesting, because the `reinterpret_cast` interpretation might be undesirable. EWG is requested to offer guidance, preferably in the form of a comprehensive mental model. The current model "static_cast, then const_cast" might not be appropriate.

See paper issue 1970.

#### 2786. Comparing pointers to complete objects

Section: 7.6.10 [expr.eq] Status: open Submitter: Alisdair Meredith Date: 2023-08-23

Subclause 7.6.10 [expr.eq] bullet 3.1 specifies:

> - If one pointer represents the address of a complete object, and another pointer represents the address one past the last element of a different complete object, [ Footnote: ... ] the result of the comparison is unspecified.
> - ...

This phrasing does not properly handle the case where addresses of subobjects are compared, yet those subobjects happen to have the same address as their respective complete objects.

The rule in question was introduced by issue 1652 for purposes of constant evaluation: comparing a pointer to an object X with a pointer past the end of another object Y ought not to be possible during contant evaluation. However, that issue resolution also caused a change to runtime behavior, departing from the prior "address comparison" model. It turns out that implementations nowadays rely on that rule for optimization purposes (see the reflector discussion).

Possible resolution:

Change in 7.6.10 [expr.eq] bullet 3.1 as follows:

> - If one pointer points to an object and represents the address of the first byte in memory occupied by that object's a complete object, and another pointer points past the end of an object whose complete object is different and represents the address of the first byte in memory after the end of the storage occupied by that object's one past the last element of a different complete object, the result of the comparison is unspecified.
> - ...

#### 3000. Handling of cv-qualified class types in conditional operator

Section: 7.6.16 [expr.cond] Status: open Submitter: Brian Bi Date: 2025-02-02

With issue 2321, the following example was supposed to be made valid:

       struct A {};
       struct B : A {};
       using T = const B;
       A a = true ? A() : T();

When reaching 7.6.16 [expr.cond] paragraph 6, the two operands have type `A` and `const A`, thus their types differ. However, the built-in candidates for the overload resolution specified in paragraph 6 do not handle class types.

Subclause 7.6.16 [expr.cond] paragraph 6 handles the following case, but should not handle differences in const-qualification.

      struct A { operator int(); };
      struct B { operator int(); };
      int n = true ? A() : B();

Furthermore, the current wording does not handle this case:

      int const **p;
      int *const *q;
      int const * const * const &r = cond ? p : q; // Could bind directly, but currently creates a temporary.

Possible resolution [SUPERSEDED]:

1. Change in 7.6.16 [expr.cond] paragraph 4 as follows:

   > Otherwise, if the second and third operand have different types and either at least one has (possibly cv-qualified) class type, or if both are glvalues of similar types (7.3.6 [conv.qual]) and of the same value category and the same type except for cv-qualification, an attempt is made to form an implicit conversion sequence (12.2.4.2 [over.best.ics]) from each of those operands to the a type of related to the other.

   > [Note 2: Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. —end note]

   > 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 an lvalue, the target type is “lvalue reference to T2 `T3`”, where `T3` is the qualification-combined type of `T2` and `T1`; but an implicit conversion sequence can only be formed only if the reference would bind directly (9.5.4 [dcl.init.ref]) to a glvalue.
   > - If E2 is an xvalue, the target type is “rvalue reference to T2 `T3`”, where `T3` is the qualification-combined type of `T2` and `T1`; but an implicit conversion sequence can only be formed only if the reference would bind directly.
   > - 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) of similar class types:
   >     - if T2 is at least as cv-qualified as T1, the target type is T2,
   >     - otherwise, no conversion sequence is formed for this operand;
   >   - otherwise, if T2 is a base class of T1, the target type is cv1 T2, where cv1 denotes the cv-qualifiers of T1 the qualification-combined type of `T2` and `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.
   > Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand to the target type determined for the third operand, and vice versa, with the following outcome:
   > - If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
   > - If no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below.
   > - Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand the combined target type is defined as the target type of that conversion.
   > - Otherwise, if both conversion sequences can be formed and the respective target types are similar, the combined target type is defined as the qualification-combined type of the target types.
   > - Otherwise, the program is ill-formed.
   > If a combined target type is defined above, each operand is converted to the combined target type and the converted operand is operands are is used in place of the original operand operands for the remainder of this subclause. [Note 3: The conversion might can be ill-formed even if an implicit conversion sequence could can be formed. —end note]

2. Change 7.6.16 [expr.cond] paragraph 6 as follows:

   > Otherwise, the result is a prvalue. If the second and third operands do not have the same type and either at least one has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (12.2.2.3 [over.match.oper], 12.5 [over.built]). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this subclause.

3. Append a row to the table in 12.2.2.3 [over.match.oper] paragraph 2 as follows:

   Subclause | Expression | As member function | As non-member function
   12.5 [over.built] | `a ? b : c` |  | `operator?:(true, b, c)`

4. Change in 12.2.2.3 [over.match.oper] paragraph 3 as follows:

   > For a unary operator @ with an operand of type cv1 T1, and for a binary operator @ with a left operand of type cv1 T1 and a right operand of type cv2 T2, and for the conditional operator, four sets of candidate functions, designated member candidates, non-member candidates, built-in candidates, and rewritten candidates, are constructed as follows:

Possible resolution:

1. Change in 7.6.16 [expr.cond] paragraph 4 as follows:

   > Otherwise, if the second and third operand have different types and either at least one has (possibly cv-qualified) class type, or if both are glvalues of similar types (7.3.6 [conv.qual]) and of the same value category and the same type except for cv-qualification, an attempt is made to form an implicit conversion sequence (12.2.4.2 [over.best.ics]) from each of those operands to the a type of related to the other.

   > [Note 2: Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. —end note]

   > 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 an lvalue a glvalue, the target type is “lvalue reference to T2 `T3`”, where
   >   - the reference is an lvalue reference if `E2` is an lvalue and an rvalue reference otherwise and
   >   - if `T2` is reference-related to `T1` (9.5.4 [dcl.init.ref]), `T3` is such that "pointer to `T3`" is the qualification-combined type of "pointer to `T2`" and "pointer to `T1`", otherwise `T3` is `T2`;
   >   but an implicit conversion sequence can only be formed only if the reference would bind directly to a glvalue.
   > - If E2 is an xvalue, the target type is “rvalue reference to T2”, but an implicit conversion sequence can only be formed if the reference would bind directly.
   > - If E2 is a prvalue or if neither of the conversion sequences sequence above can cannot 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): `T2` is reference-related to `T1`, the target type `T3` is such that "pointer to `T3`" is the qualification-combined type of "pointer to `T2`" and "pointer to `T1`";
   >     - if T2 is at least as cv-qualified as T1, the target type is T2,
   >     - otherwise, no conversion sequence is formed for this operand;
   >   - 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.
   > Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand to the target type determined for the third operand, and vice versa, with the following outcome:
   > - If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
   > - If no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below.
   > - Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand the common target type is defined as the target type of that conversion.
   > - Otherwise, if both conversion sequences can be formed and the target types are the same, the common target type is defined as that type.
   > - Otherwise, if both conversion sequences can be formed and one of the target types is "reference to `T`" and the other is `T` for some type `T`, the common target type is defined as `T`.
   > - Otherwise, the program is ill-formed.
   > If a common target type is defined above, each operand is converted to the common target type and the converted operand is operands are is used in place of the original operand operands for the remainder of this subclause. [Note 3: The conversion might can be ill-formed even if an implicit conversion sequence could can be formed. —end note]

2. Change 7.6.16 [expr.cond] paragraph 6 as follows:

   > Otherwise, the result is a prvalue. If the second and third operands do not have the same type and either at least one has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (12.2.2.3 [over.match.oper], 12.5 [over.built]). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this subclause.

3. Add an example at the end of 7.6.16 [expr.cond] as follows:

   >     template<class T>
   >     T f();

   >     using X0 = decltype(true ? f<int const **&>() : f<int *const *&>());  // X0 is "reference to const pointer to const pointer to const int"

   >     struct B {};
   >     using X1 = decltype(true ? f<const B&>() : f<volatile B&>());  // X1 is "lvalue reference to const volatile B"
   >     using X2 = decltype(true ? f<const B&>() : f<B>());      // X2 is const B

   >     struct C {
   >       operator B&() const;
   >     };
   >     using X3 = decltype(true ? f<const C>() : f<B&>());      // X3 is "lvalue reference to B"

   >     struct D : B {};
   >     using X4 = decltype(true ? f<const D&>() : f<B&>());       // X4 is const B&
   >     using X5 = decltype(true ? f<const D>() : f<B>());       // X5 is const B

4. Add a new paragraph before 12.2.2.3 [over.match.oper] paragraph 5 as follows:

   > For the conditional operator, overload resolution is performed only as specified in 7.6.16 [expr.cond]; the candidate functions for the expression `a ? b : c` are the built-in candidates for the hypothetical function call `operator?:(true, b, c)` (12.5 [over.built]).

   > For the first parameter of the built-in assignment operators, only standard conversion sequences (12.2.4.2.2 [over.ics.scs]) are considered.

The resolution above also adresses issues 2023 and 2865; the resolution applied for the latter yields a const lvalue, not a prvalue.

#### 1542. Compound assignment of braced-init-list

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

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

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

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

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

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

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

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

      char c;
      c += {1};

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

Notes from the October, 2012 meeting:

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

See also issue 2399.

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

#### 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:

      union A {
        constexpr A(int) : x(++x) { }
        int x;
        char* y;
      };
      union B {
        A a = 5;
      };
      int arr[B().a.x];

Value-initialization of the object created by `B()` zero-initializes the object (9.5 [dcl.init] 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:

      struct A {
        constexpr A(int i) : val(i) { }
        constexpr operator int() const { return val; }
        constexpr operator float() const { return val; }
      private:
        int val;
      };
      constexpr A a = 42;
      int ary[a];

According to 9.3.4.5 [dcl.array] 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 [expr.const] 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.5.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 [expr.new] 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:

      struct A { int x = 1; int y; };
      constinit A a;                   // static storage duration; #1

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 [expr.const] 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.10.3.2 [basic.start.static] 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.8.6.2 [basic.stc.static]) or thread storage duration (6.8.6.3 [basic.stc.thread]) is zero-initialized (9.5 [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 issue 2558.

Suggested resolution [SUPERSEDED]:

Change in 7.7 [expr.const] 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 [expr.const] 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.10.3.2 [basic.start.static] 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.8.6.2 [basic.stc.static]) or thread storage duration (6.8.6.3 [basic.stc.thread]) or a subobject thereof is zero-initialized (9.5 [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.10.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:

      struct C {
        int a;
        int b;
        C() : b(a) {}   // #1
      };
      constinit C x;    // OK when zero-initializing first, because #1 reads zero-initialized a?

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 [expr.const] 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<T>` 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.8.4 [basic.life] paragraph 8) during constant evaluation to satisfy 7.7 [expr.const] 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,

      struct A {
        int x, y;
      };
      struct B {
        float a;
        int b;
      };
      union C {
        A a;
        B b;
      };
      constexpr int f() {
       C c = {};
       std::construct_at(&c.b.b, 5);
       // Does this return 5 if c.a.y and c.b.b are laid out at the same address?
       return c.a.y;
      }
      static_assert(f());  // not a constant-expression

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()`:

      #include <memory>

      struct A {
        virtual constexpr char f() { return 'A'; }
      };
      struct B : A {
        constexpr char f() override { return 'B'; }
      };

      constexpr char f() {
        B b;
        A *p = &b;
        std::construct_at(p);
        return p->f();     // #1; alternative: return b.f()
      }
      static_assert(f() == 'A');   // error: #1 accesses out-of-lifetime object

See also issue 2866 for concerns about observing the effects of an attribute if the potentially-overlapping subobject was introduced via `[[no_unique_address]]`.

#### 2559. Defaulted consteval functions

Section: 7.7 [expr.const] Status: open Submitter: Aaron Ballman Date: 2022-03-29

Consider:

      template <typename Ty>
      struct S {
        Ty i;
        consteval S() = default;
      };

      template <typename Ty>
      struct T {
        Ty i;
        consteval T() {}
      };

      S<int> one; // only Clang rejects
      T<int> two; // Clang, GCC, ICC, MSVC reject

      void locals() {
        S<int> three; // only Clang rejects
        T<int> four;  // Clang, GCC, ICC, MSVC reject
      }

A consteval function should always be evaluated at compile time and never fall back to runtime, thus all four cases should be rejected. Issue 2558 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 [expr.const] paragraph 7:

    extern Swim dc;
    constexpr auto& sandeno  = typeid(dc);     // OK, can only be typeid(Swim)

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 [expr.const] 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 [expr.const] 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;

#### 2702. Constant destruction of reference members

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2023-02-13

Issue 2529 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:

      consteval int a(auto);
      constexpr int g(auto e);
      constexpr int f(auto e);

      constexpr int f(auto e) {
        return a(e);
      }

      auto ff = f<int>;   // error: f is an immediate function

      auto gg = g<int>;   // ok?

      constexpr int g(auto e) { // g is not an immediate function?
        return a(e);
      }

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 [temp.inst] 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 issue 2497.

#### 2740. Too many objects have constexpr-unknown type

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2023-02-10

Consider:

      struct A {
        A();
        virtual constexpr int f() { return 1; }
      };
      struct B : A {
        virtual constexpr int f() { return 2; }
      };
      B b; // not constexpr
      auto &id = typeid(b); // error, not constant

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.

#### 2962. Evaluation of destructor call for variable with constant destruction

Section: 7.7 [expr.const] Status: open Submitter: Cody Miller Date: 2024-11-23

Consider:

      #include <type_traits>

      extern "C" int puts(const char*);

      struct S {
        consteval S(int i) : buf{i} {}
        constexpr ~S() {
          if (!std::is_constant_evaluated())
    	puts("Destroyed\n");
        }
        int buf;
      };

      int main() {
        constexpr S s{43};
        return s.buf;
      }

The destruction should be manifestly constant-evaluated.

Also consider:

      struct A {
        constexpr A() {
          if !consteval { puts("constructed"); }
        }
        constexpr ~A() {
          if !consteval { puts("destroyed"); }
        }
      };

      void f() {
        { A a; }
        delete new A;
        A();
      }

      void g() {
        constexpr A a;      // Usable in constant expressions: destruction is manifestly constant evaluated.
      }
      A a;      // Constant-initialized, but destruction is not manifestly constant evaluated.

Possible resolution:

Change in 7.7 [expr.const] paragraph 21 as follows:

> An expression or conversion is manifestly constant-evaluated if it is:
> - ...
> - the initializer of a variable that is usable in constant expressions or has constant initialization (6.10.3.2 [basic.start.static]). , [ Footnote: ... ] or [ Example: ... ]
> - the destruction of an object that is usable in constant expressions and has constant destruction.

#### 3004. Pointer arithmetic on array of unknown bound

Section: 7.7 [expr.const] Status: open Submitter: A. Jiang Date: 2025-03-07

(From submission #680.)

Consider:

      extern const int arr[];
      constexpr const int *p = arr + N;  // #1
      constexpr int arr[2] = {0, 1};     // #2
      constexpr int k = *p;              // #3

What is the outcome here? Accept for N == 0 and otherwise #1 is non-constant (clang)? Always accept #1, but reject #3 for out-of-bounds accesses (gcc)? Always accept #1, but always reject #3, even for in-bound accesses (EDG)? Rejecct #2 (MSVC)?

Further, we miss a specification when a both a declaration of type "array of unknown bound of T" as well as one of the type "array of N T" is reachable. We should take the latter as the type.

Possible resolution:

1. Add a bullet after 7.7 [expr.const] bullet 10.15 as follows:

   > - ...
   > - a reinterpret_cast (7.6.1.10 [expr.reinterpret.cast]);
   > - pointer arithmetic (7.6.6 [expr.add]) where one (possibly converted) operand points to the first element of an array of unknown bound and the other (possibly converted) operand is of integral type with non-zero value
   > - ...

2. Change in 6.9.1 [basic.types.general] paragraph 6 as follows:

   > A class type (such as “class X”) can be incomplete at one point in a translation unit and complete later on; the type “class X” is the same type at both points. The declared type of an array object variable can be an array of incomplete class type `C` and therefore incomplete; if the class type is completed later on wherever a definition of `C` is reachable in the translation unit, the array type becomes is complete; the array type at those two points is the same type. The declared type of an array object variable `v` can be an array of unknown bound and therefore be incomplete at one point in a translation unit and is complete later on wherever a declaration of `v` with a known bound is reachable; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.

#### 3009. Unclear rules for constant initialization

Section: 7.7 [expr.const] Status: open Submitter: Jim X Date: 2023-11-08

(From submission #460.)

Consider:

      struct B {
        constexpr ~B(){}      // #1
      };
      struct A {
        constexpr A(const B&){}  // #2
        constexpr ~A(){}
      };
      constexpr A a(B{});  // #3, checks #1 and #2

Subclause 7.7 [expr.const] bullet 6.1 specifies:

> A variable v is constant-initializable if
> - the full-expression of its initialization is a constant expression when interpreted as a constant-expression...
> - ...

However, a full-expression need not satisfy the syntax of a constant-expression, and indeed, for the example at #3, the only syntactical expression is `B{}`, but the checking of the constructor of `A` and the destructor of `B` is intended to be part of the conditions for constant-initializable, which unsuccessfully attempts to delegate to 7.7 [expr.const] paragraph 22.

#### 3010. constexpr placement-new should require transparent replaceability

Section: 7.7 [expr.const] Status: open Submitter: Richard Smith Date: 2025-03-16

Consider:

      struct A { int n; };
      struct B : A { int m; };

      constexpr int f() {
       B b = {{0}, 0};
       A *p = &b;
       new (p) A{1};     // does not transparently replace *p
       return p->n;      // UB, p refers to out-of-lifetime object
      }
      constexpr int k = f();

It seems unreasonable to expect implementations to track situations where a placement-new evaluations that don't transparently replace the original object.

The status quo wording in 7.7 [expr.const] bullet 10.18.2 involving "similar" was added by issue 2922.

Suggested resolution:

Change in 7.7 [expr.const] bullet 10.18.2 as follows:

> An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.10.1 [intro.execution]), would evaluate one of the following:
> - ...
> - a new-expression (7.6.2.8 [expr.new]), unless either
>   - the selected allocation function is a replaceable global allocation function (17.6.3.2 [new.delete.single], 17.6.3.3 [new.delete.array]) and the allocated storage is deallocated within the evaluation of E, or
>   - the selected allocation function is a non-allocating form (17.6.3.4 [new.delete.placement]) with an allocated type T, where
>     - the placement argument to the new-expression points to an object whose type is similar to T (7.3.6 [conv.qual]) that is transparently replaceable (6.8.4 [basic.life]) by the object created by the new-expression or, if T is an array type, to the first element of such an object of a type similar to T, and
>     - the placement argument points to storage whose duration began within the evaluation of E;
> - ...

#### 3059. `throw;` in constant expressions

Section: 7.7 [expr.const] Status: open Submitter: Lénárd Szolnoki Date: 2025-08-28

(From submission #756.)

Consider:

      int main() {
        try {
          throw 0;
        } catch (...) {
          constexpr int x = [] {
           try {
             throw;          // #1
           } catch (...)
             return 1;
           }
          }();
        }
      }

According to 7.6.18 [expr.throw] paragraph 3, #1 rethrows the currently handled exception as defined in 14.4 [except.handle] paragraph 10, which would rethrow the runtime exception inside a constant expression.

Suggested resolution:

Add a new bullet after 7.7 [expr.const] bullet 10.22 as follows:

> - ...
> - a construction of an exception object, unless the exception object and all of its implicit copies created by invocations of std::current_exception or std::rethrow_exception (17.9.7 [propagation]) are destroyed within the evaluation of E;
> - a throw-expression (7.6.18 [expr.throw]) with no operand, unless there is a currently handled exception whose exception object was constructed within the evaluation of E;
> - ...

#### 3044. Iterating expansion statements woes

Section: 8.7 [stmt.expand] Status: open Submitter: Jakub Jelinek Date: 2025-07-08

(From submissions #725 and #730.)

The rewrite of iterating expansion statements contains:

      static constexpr auto iter = begin + i;

where the type of `i` is unspecified, but overload resolution of `+` depends on the type of `i`.

Furthermore, the rewrite contains

      for (auto i = begin ; i != end ; ++i, ++result);

which might invoke an overloaded comma operator, which is undesirable.

Furthermore, because of the use of `static` in the rewrite, the example in 8.7 [stmt.expand] paragraph 7 is ill-formed.

Possible resolution:

1. Change in 8.7 [stmt.expand] bullet 5.2 as follows:

   > Otherwise, if S is an iterating expansion statement, S is equivalent to:

   >       {
   >         init-statement
   >         static constexpr auto&& range = expansion-initializer ;
   >         static constexpr auto begin = begin-expr;    // see 8.6.5 [stmt.ranged]
   >         static constexpr auto end = end-expr;        // see 8.6.5 [stmt.ranged]
   >         S0
   >         .
   >         .
   >         .
   >         SN-1
   >       }

   > where N is the result of evaluating the expression

   >       [] consteval {
   >         std::ptrdiff_t result = 0;
   >         for (auto i = begin ; i != end ; ++i, ++result) ++result;
   >         return result;    // distance from begin to end
   >       }()

   > and Si is

   >       {
   >         static constexpr auto iter = begin + i i;
   >         for-range-declaration = *iter ;
   >         compound-statement
   >       }

   > The variables range , begin , end , and iter are defined for exposition only. The identifier `i` is considered to be a prvalue of type `decltype(begin - begin)`; the program is ill-formed if i is not representable as such a value. [Note 1 : The instantiation is ill-formed if range is not a constant expression (7.7 [expr.const]). -- end note]

2. No change in 8.7 [stmt.expand] paragraph 7:

   > [ Example 2:

   >       consteval int f() {
   >         constexpr std::array<int, 3> arr {1, 2, 3};
   >         int result = 0;
   >         template for (constexpr int s : arr) {      // OK, iterating expansion statement
   >           result += sizeof(char[s]);
   >         }
   >         return result;
   >       }
   >       static_assert(f() == 6);

   > —end example]

#### 3048. Empty destructuring expansion statements

Section: 8.7 [stmt.expand] Status: open Submitter: Jakub Jelinek Date: 2025-08-09

(From submission #743.)

Consider:

    struct S { };
    void f() {
      S s;
      template for (auto x : s) { }
    }

This destructuring expansion statement is ill-formed, because the syntactic rewrite contains

      auto&& [] = s ;

which is an ill-formed structured binding declaration.

Empty expansion statements are allowed for the other two kinds of expansion statements.

Possible resolution:

Change in 8.7 [stmt.expand] bullet 5.3 as follows:

> - Otherwise, S is a destructuring expansion statement and, if N is 0, S is equivalent to:

>       init-statement
>       constexpropt auto&& range = expansion-initializer ;

>   otherwise, S is equivalent to: ...

#### 3061. Trailing comma in an expansion-init-list

Section: 8.7 [stmt.expand] Status: open Submitter: Jan Schultke Date: 2025-08-24

(From submission #754.)

Brace-enclosed lists generally allow trailing commas. But an expansion-init-list does not; consider:

               for (int x : { 1, }) { }   // OK
      template for (int x : { 1, }) { }   // syntax error

Suggested resolution:

Change in 8.7 [stmt.expand] paragraph 1 as follows:

      expansion-init-list :
           { expression-list ,opt }
           { }

#### 2115. Order of implicit destruction vs release of automatic storage

Section: 8.8 [stmt.jump] Status: open 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.

#### 2495. Glvalue result of a function call

Section: 8.8.4 [stmt.return] Status: open Submitter: Jim X Date: 2021-07-04

According to 8.8.4 [stmt.return] 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.5 [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.5 [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.

#### 2995. Meaning of flowing off the end of a function

Section: 8.8.4 [stmt.return] Status: open Submitter: Brian Bi Date: 2025-02-16

Consider:

      int foo() {
        struct S {
          ~S() noexcept(false) { throw 1; }
        } s;
      }

      int main() {
        try {
          foo();
        } catch (int x) {
          return x;
        }
      }

Does this program exit with code 1, or does it have undefined behavior per 8.8.4 [stmt.return] paragraph 4? Implementations differ.

The crucial question is whether the undefined behaviour caused by "flowing off the end" is before or after the closing brace of the function body, which is where the destruction of local variables occurs. Current consensus appears to solidify around "after".

This approach is consistent with the observation that the following two functions, given identical (valid) function bodies, should have the same semantics:

      void f() {{ ... }}
      void g() { ... }

Suggested resolution (option 1):

1. Change in 6.10.3.1 [basic.start.main] paragraph 5 as follows:

   > A return statement (8.8.4 [stmt.return]) in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std::exit with the return value as the argument. If control flows off the end of the compound-statement of main, the effect is equivalent to a return with operand 0 (see also 14.4 [except.handle]).

2. Add a new paragraph at the end of 8.4 [stmt.block]:

   > [Note 1: A compound statement defines a block scope (6.4 [basic.scope]). A declaration is a statement (8.10 [stmt.dcl]). —end note]

   > Control is said to reach the end of a compound-statement

   > - when its last statement completes normally and is not a jump-statement (8.8 [stmt.jump]) and the destruction of block variables with automatic storage duration, if any, completes normally (8.10 [stmt.dcl]), or
   > - when the compound-statement is executed and contains no statements.

3. Change in 8.8.4 [stmt.return] paragraph 4 as follows:

   > Control is said to flow off the end of a function when control reaches the end of its compound-statement or that of a handler of its function-try-block (8.4 [stmt.block]). Flowing off the end of a constructor, a destructor, or a non-coroutine function with a cv void return type is equivalent to a return with no operand returns control to the caller, except as specified in 14.4 [except.handle]. Otherwise, flowing off the end of a function that is neither main (6.10.3.1 [basic.start.main]) nor a coroutine (9.6.4 [dcl.fct.def.coroutine]) results in undefined behavior.

4. Change in 8.8.5 [stmt.return.coroutine] paragraph 3 as follows:

   > If a search for the name `return_void` in the scope of the promise type finds any declarations, flowing off reaching the end of a coroutine's original function-body (8.4 [stmt.block]) is equivalent to a co_return with no operand; otherwise flowing off reaching the end of a coroutine's original function-body results in undefined behavior.

5. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 6 as follows:

   > [Note 1: If `return_void` is found, flowing off reaching the end of a coroutine coroutine's original function-body (8.4 [stmt.block]) is equivalent to a `co_return` with no operand. Otherwise, flowing off reaching the end of a coroutine coroutine's original function-body results in undefined behavior (8.8.5 [stmt.return.coroutine]). —end note]

6. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 11 as follows:

   > The coroutine state is destroyed when control flows off the end of the coroutine (8.8.4 [stmt.return]) or the destroy member function (17.13.4.6 [coroutine.handle.resumption]) of a coroutine handle (17.13.4 [coroutine.handle]) that refers to the coroutine is invoked. In the latter case, control in the coroutine is considered to be transferred out of the function (8.10 [stmt.dcl]). The storage for the coroutine state is released by calling a non-array deallocation function (6.8.6.5.3 [basic.stc.dynamic.deallocation]). If destroy is called for a coroutine that is not suspended, the program has undefined behavior.

7. Change in 14.4 [except.handle] paragraph 14 as follows:

   > The currently handled exception is rethrown if control reaches the end (8.4 [stmt.block]) of a handler of the function-try-block of a constructor or destructor. Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see 8.8.4 [stmt.return]).

Possible resolution (option 2):

1. Change in 6.10.3.1 [basic.start.main] paragraph 5 as follows:

   > A return statement (8.8.4 [stmt.return]) in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std::exit with the return value as the argument. If control flows off the end of the compound-statement of main, the effect is equivalent to a return with operand 0 (see also 14.4 [except.handle]). The statement `return 0;` is appended to the outermost blocks of `main` (8.4 [stmt.block]).

2. Add a new paragraph at the end of 8.4 [stmt.block]:

   > [Note 1: A compound statement defines a block scope (6.4 [basic.scope]). A declaration is a statement (8.10 [stmt.dcl]). —end note]

   > The outermost blocks of a function are

   > - the compound-statement of its function-body or function-try-block (if any) and
   > - the compound-statements of the handlers of its function-try-block (if any).
   > [Note: A defaulted or deleted function has no outermost blocks. -- end note]

   > A statement S is appended to a compound-statement by inserting S immediately prior to the closing `}`.

   > The statement `throw;` is appended to the compound-statements of the handlers of a function-try-block of a constructor or destructor. The statement `return;` is appended to any other outermost block of a non-coroutine function with a cv `void` return type or with no return type.

   > If control flows off the end of an outermost block of a function and the destruction of block variables with automatic storage duration, if any, completes normally (8.10 [stmt.dcl]), the behavior is undefined. [Note: Control never flows off the end of `main` (6.10.3.1 [basic.start.main]), a constructor, a destructor, or a void-returning function. -- end note]

3. Change in 8.8.4 [stmt.return] paragraph 4 as follows:

   > Flowing off the end of a constructor, a destructor, or a non-coroutine function with a cv void return type is equivalent to a return with no operand. Otherwise, flowing off the end of a function that is neither main (6.10.3.1 [basic.start.main]) nor a coroutine (9.6.4 [dcl.fct.def.coroutine]) results in undefined behavior.

4. Change in 8.8.5 [stmt.return.coroutine] paragraph 3 as follows:

   > If 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 offthe end of a coroutine's function-body results in undefined behavior.

5. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 6 as follows:

   > 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. Otherwise, if the search for the name `return_void` in the scope of the promise type finds any declarations, the statement `co_return;` is appended to the coroutine's original function-body (8.4 [stmt.block], 8.8.5 [stmt.return.coroutine]). [Note 1: If `return_void` is found, flowing off the end of a coroutine is equivalent to a `co_return` with no operand. Otherwise, flowing Flowing off the end of a coroutine coroutine's original function-body results in undefined behavior (8.8.5 [stmt.return.coroutine]). —end note]

6. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 11 as follows:

   > The coroutine state is destroyed when control flows off the end of the coroutine at the end of its replacement function-body (8.4 [stmt.block]) or when the destroy member function (17.13.4.6 [coroutine.handle.resumption]) of a coroutine handle (17.13.4 [coroutine.handle]) that refers to the coroutine is invoked. In the latter case, control in the coroutine is considered to be transferred out of the function (8.10 [stmt.dcl]). The storage for the coroutine state is released by calling a non-array deallocation function (6.8.6.5.3 [basic.stc.dynamic.deallocation]). If destroy is called for a coroutine that is not suspended, the program has undefined behavior.

7. Change in 14.4 [except.handle] paragraph 14 as follows:

   > The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see 8.8.4 [stmt.return]).

#### 3052. Unclear handling of checks on discarded `return` statements

Section: 8.8.4 [stmt.return] Status: open Submitter: Richard Smith Date: 2025-08-02

(From submission #737.)

Consider:

      auto f() {
        if constexpr (false) return;
        return 3;
      }

Is this well-formed? The first `return` statement is discarded, so the return type is not deduced from it. However, 8.8.4 [stmt.return] paragraph 2 still seems to apply; discarded return statements are not excluded:

> ... A return statement with no operand shall be used only in a function whose return type is cv void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]). A return statement with an operand of type void shall be used only in a function that has a cv void return type. A return statement with any other operand shall be used only in a function that has a return type other than cv void; ...

Suggested resolution:

Change in 8.8.4 [stmt.return] paragraph 2 as follows:

> ... A return statement with no operand shall be used appear as a non-discarded statement (8.5.2 [stmt.if]) only in a function whose return type is cv void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]). A return statement with an operand of type void shall be used appear as a non-discarded statement only in a function that has a cv void return type. A return statement with any other operand shall be used appear as a non-discarded statement only in a function that has a return type other than cv void; such a non-discarded return statement initializes the returned reference or prvalue result object of the (explicit or implicit) function call by copy-initialization (9.5 [dcl.init]) from the operand.

#### 2123. Omitted constant initialization of local static variables

Section: 8.10 [stmt.dcl] Status: open Submitter: Hubert Tong Date: 2015-02-02

According to 8.10 [stmt.dcl] paragraph 4,

> The zero-initialization (9.5 [dcl.init]) of all block-scope variables with static storage duration (6.8.6.2 [basic.stc.static]) or thread storage duration (6.8.6.3 [basic.stc.thread]) is performed before any other initialization takes place. Constant initialization (6.10.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:

      constexpr int x = 0;

      auto foo() {
        constexpr static const int *p = &x;
        struct A {
          const int *const &getPtr() { return p; }
        } a;
        return a;
      }

      int xcpy = *decltype(foo()){ }.getPtr();

      int main(void) {
        return xcpy;
      }

For a related example, consider:

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

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

Here, `b` may or may not have constant initialization, but we don't have an ODR violation.

If we want to support such code, the nicest option would be to say that the ODR requires us to act as if we pick one of the definitions of the inline function, which requires us to make a consistent choice for all static storage duration variables within a given function. Alternatively, we could say that if multiple definitions of a variable disagree over whether it has constant initialization, then it does not, allowing more implementation simplicity and no functional change outside of pathological cases.

Notes from the February, 2016 meeting:

The second example will be dealt with separately under issue 2242. For the first example, the Standard should require that local types can be used outside their function only via a returned object. It was still to be decided whether this should be undefined behavior or an error on use of such a type. It was also noted that the same issue can arise with static member functions.

#### 2963. Paradoxical variable-or-function declaration

Section: 8.11 [stmt.ambig] Status: open Submitter: Davis Herring Date: 2024-11-17

(From submission #643.)

Consider:

      struct A {
        static int b;
      };
      struct B {
        B(int);
        B operator()();
        using b=int;
      };

      namespace P {
        B A(decltype(A())::b);
      }

If `P::A` is a variable, then `decltype(A())::b` is its initializer, and the `A` therein refers to the variable. That implies that the `decltype` is `B` and `B::b` is `int`, thus the declaration should be a function declaration instead.

If `P::A` is a function, then its name is not available for lookup in its parameter list (see 6.4.2 [basic.scope.pdecl] paragraph 1), so `A` is `::A` and `A::b` is a varaible, thus the declaration should be a variable declaration instead.

Implementations agree that `P::A` is a variable, but use the interpretation of `decltype(A())` that finds `::A`, presumably from some trial parse.

Suggested resolution:

Extend the prohibition on using `A` in a template argument (8.11 [stmt.ambig] paragraph 3 to `decltype`.

#### 157. Omitted typedef declarator

Section: 9.1 [dcl.pre] Status: open Submitter: Daveed Vandevoorde Date: 19 Aug 1999

9.1 [dcl.pre] paragraph 3 reads,

> In a simple-declaration, the optional init-declarator-list can be omitted only when... the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (11.3 [class.name] ), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in those specifiers are among the names being declared by the declaration... In such cases, and except for the declaration of an unnamed bit-field (11.4.10 [class.bit] ), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration. [Example:

>         enum { };           // ill-formed
>         typedef class { };  // ill-formed

> —end example]

In the absence of any explicit restrictions in 9.2.4 [dcl.typedef] , this paragraph appears to allow declarations like the following:

        typedef struct S { };    // no declarator
        typedef enum { e1 };     // no declarator

In fact, the final example in 9.1 [dcl.pre] paragraph 3 would seem to indicate that this is intentional: since it is illustrating the requirement that the decl-specifier-seq must introduce a name in declarations in which the init-declarator-list is omitted, presumably the addition of a class name would have made the example well-formed.

On the other hand, there is no good reason to allow such declarations; the only reasonable scenario in which they might occur is a mistake on the programmer's part, and it would be a service to the programmer to require that such errors be diagnosed.

#### 498. Storage class specifiers in definitions of class members

Section: 9.2.2 [dcl.stc] Status: open Submitter: Matt Austern Date: 13 Jan 2005

Suppose we've got this class definition:

        struct X {
           void f();
           static int n;
        };

I think I can deduce from the existing standard that the following member definitions are ill-formed:

        static void X::f() { }
        static int X::n;

To come to that conclusion, however, I have to put together several things in different parts of the standard. I would have expected to find an explicit statement of this somewhere; in particular, I would have expected to find it in 9.2.2 [dcl.stc]. I don't see it there, or anywhere.

Gabriel Dos Reis: Or in 6.7 [basic.link] which is about linkage. I would have expected that paragraph to say that that members of class types have external linkage when the enclosing class has an external linkage. Otherwise 6.7 [basic.link] paragraph 8:

> Names not covered by these rules have no linkage.

might imply that such members do not have linkage.

Notes from the April, 2005 meeting:

The question about the linkage of class members is already covered by 6.7 [basic.link] paragraph 5.

#### 2232. `thread_local` anonymous unions

Section: 9.2.2 [dcl.stc] Status: open Submitter: Mike Herrick Date: 2016-02-23

It is not clear from the current wording whether the `thread_local` specifier can be applied to anonymous unions or not. According to 9.2.2 [dcl.stc] paragraph 3,

> The `thread_local` specifier indicates that the named entity has thread storage duration (6.8.6.3 [basic.stc.thread]). It shall be applied only to the names of variables of namespace or block scope and to the names of static data members.

One might think that an anonymous union object would be a “variable,” but the next paragraph seems to treat variables and anonymous unions as distinct:

> The `static` specifier can be applied only to names of variables and functions and to anonymous unions (11.5.2 [class.union.anon]).

#### 2973. Does an alias-declaration introduce a name for linkage purposes?

Section: 9.2.4 [dcl.typedef] Status: open Submitter: Jonathan Wakely Date: 2024-12-19

Consider:

       using A = struct { int i; };

Does the unnamed class have `A` as its name for linkage purposes? There is implementation divergence.

Possible resolution (option 1):

1. Change in 6.7 [basic.link] 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 or alias-declaration in which the class has the typedef name for linkage purposes (9.2.4 [dcl.typedef]); or
   > - a named enumeration (9.8.1 [dcl.enum]), or an unnamed enumeration defined in a typedef declaration or alias-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.8.1 [dcl.enum]); or
   > - a template
   > has its linkage determined as follows: ...

2. Change in 6.7 [basic.link] paragraph 5 as follows:

   > In addition, a member function, a static data member, a named class or enumeration that inhabits a class scope, or an unnamed class or enumeration defined in a typedef declaration or alias-declaration that inhabits a class scope such that the class or enumeration has the typedef name for linkage purposes (9.2.4 [dcl.typedef]), has the same linkage, if any, as the name of the class of which it is a member.

3. Change in 6.9.1 [basic.types.general] paragraph 6 as follows:

   > ... [Note 3: The type of a pointer or reference to array of unknown bound permanently points to or refers to an incomplete type. An array of unknown bound named by a typedef declaration or alias-declaration permanently refers to an incomplete type. In either case, the array type cannot be completed. —end note] ...

4. Change in 9.2.4 [dcl.typedef] paragraph 4 as follows:

   > An unnamed class or enumeration C defined in a typedef declaration or alias-declaration has the first typedef-name declared by the declaration to be of type C as its typedef name for linkage purposes (6.7 [basic.link]). [Note 2: A typedef declaration or alias-declaration involving a lambda-expression does not itself define the associated closure type, and so the closure type is not given a typedef name for linkage purposes. —end note] [Example 4:

   >       typedef struct { } *ps, S;    // S is the typedef name for linkage purposes
   >       typedef decltype([]{}) C;     // the closure type has no typedef name for linkage purposes
   >       using U = class { };          // U is the typedef name for linkage purposes

   > —end example]

Possible resolution (option 2):

1. Change in 6.2 [basic.def] paragraph 2 as follows:

   > Each entity declared by a declaration is also defined by that declaration unless:
   > - ...
   > - it is a typedef an alias declaration (9.2.4 [dcl.typedef]),
   > - it is an alias-declaration (9.2.4 [dcl.typedef]),
   > - ...

2. Change in 6.7 [basic.link] 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 an alias declaration in which the class has the typedef name for linkage purposes (9.2.4 [dcl.typedef]); or
   > - a named enumeration (9.8.1 [dcl.enum]), or an unnamed enumeration defined in a typedef an alias 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.8.1 [dcl.enum]); or
   > - a template
   > has its linkage determined as follows: ...

3. Change in 6.7 [basic.link] paragraph 5 as follows:

   > In addition, a member function, a static data member, a named class or enumeration that inhabits a class scope, or an unnamed class or enumeration defined in a typedef an alias declaration that inhabits a class scope such that the class or enumeration has the typedef name for linkage purposes (9.2.4 [dcl.typedef]), has the same linkage, if any, as the name of the class of which it is a member.

4. Change in 6.9.1 [basic.types.general] paragraph 6 as follows:

   > ... [Note 3: The type of a pointer or reference to array of unknown bound permanently points to or refers to an incomplete type. An array of unknown bound named by a typedef an alias declaration (9.2.4 [dcl.typedef]) permanently refers to an incomplete type. In either case, the array type cannot be completed. —end note] ...

5. Change in 9.1 [dcl.pre] paragraph 5 as follows:

   > If the decl-specifier-seq contains the `typedef` specifier, see 9.2.4 [dcl.typedef]. the declaration is a typedef declaration and each declarator-id is declared to be a typedef-name, synonymous with its associated type (9.2.4 [dcl.typedef]). [Note 4: Such a declarator-id is an identifier (11.4.8.3 [class.conv.fct]). —end note] Otherwise, if the type associated with a declarator-id is a function type (9.3.4.6 [dcl.fct]), the declaration is a function declaration. Otherwise, if the type associated with a declarator-id is an object or reference type, the declaration is an object declaration. Otherwise, the program is ill-formed.

6. Add a new paragraph after 9.2.4 [dcl.typedef] paragraph 2 as follows:

   > ... The defining-type-specifier-seq of the defining-type-id shall not define a class or enumeration if the alias-declaration is the declaration of a template-declaration.

   > An alias declaration is a typedef declaration or an alias-declaration.

7. Change in 9.2.4 [dcl.typedef] paragraph 4 as follows:

   > An unnamed class or enumeration C defined in a typedef an alias declaration has the first typedef-name declared by the declaration to be of type C as its typedef name for linkage purposes (6.7 [basic.link]). [Note 2: A typedef An alias declaration involving a lambda-expression does not itself define the associated closure type, and so the closure type is not given a typedef name for linkage purposes. —end note] [Example 4:

   >       typedef struct { } *ps, S;    // S is the typedef name for linkage purposes
   >       typedef decltype([]{}) C;     // the closure type has no typedef name for linkage purposes
   >       using U = class { };          // U is the typedef name for linkage purposes

   > —end example]

#### 3012. Deviating `constexpr` or `consteval` across translation units

Section: 9.2.6 [dcl.constexpr] Status: open Submitter: Benjamin Sch. Date: 2025-03-15

(From submission #683.)

Subclause 9.2.6 [dcl.constexpr] paragraph 1 specifies:

> ... If any declaration of a function or function template has a `constexpr` or `consteval` specifier, then all its declarations shall contain the same specifier.

This rule should not require a diagnostic if the declarations are not mutually reachable.

Possible resolution:

Change in 9.2.6 [dcl.constexpr] paragraph 1 as follows:

> ... If any declaration of a function or function template F has a `constexpr` or `consteval` specifier, then all its declarations shall contain the same specifier; a diagnostic is required only if a declaration of F reachable from another declaration of F deviates.

#### 2195. Unsolicited reading of trailing volatile members

Section: 9.2.9.2 [dcl.type.cv] Status: open Submitter: Hubert Tong Date: 2015-11-06

Consider:

      struct A {
        ~A();
        double d;
        float f;
      };

      struct B : A { volatile int i; };

      A foo(B *bp) { return *static_cast<A *>(bp); }

Is it okay for the memory associated with `bp->i` to be accessed by `foo`?

See also 9.2.9.2 [dcl.type.cv] paragraph 5

> The semantics of an access through a volatile glvalue are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.

Additional notes from the November, 2016 meeting:

See also national body comment CH2, addressed in March, 2017 by P0612R0.

#### 3003. Naming a deducible template for class template argument deduction

Section: 9.2.9.3 [dcl.type.simple] Status: open Submitter: Hubert Tong Date: 2025-02-02

(From submission #670.)

Consider:

      template <template <typename> class TT>
      void f() { TT x(42); }  // #1

      template <typename T> struct A { A(T); };

      void g() { f<A>(); }

Using a template template parameter should not enable class template argument deduction at #1. However, while the normative rules are clear, all implementations accept the code above, but fail (sometimes spectacularly) when parsing situations involving alias templates:

      template <typename T> struct A { A(T); };

      template <typename T, template <typename> class TT = A>
      using Alias = TT<T>;

      template <typename T>
      using Alias2 = Alias<T>;

      void h() { Alias2 a(42); }
      void h2() { Alias a(42); }

Possible resolution:

Change in 9.2.9.3 [dcl.type.simple] paragraph 3 and split into numbered paragraphs as follows:

> (3) A placeholder-type-specifier is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]).

> (4) A type-specifier of the form

>     typename/opt nested-name-specifier/opt template-name

> is a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]). The nested-name-specifier, if any, shall be non-dependent and the template-name shall name a deducible template. A deducible template is either a class template or is an alias template whose defining-type-id is of the form typenameopt nested-name-specifieropt templateopt simple-template-id where the nested-name-specifier (if any) is non-dependent and the template-name of the simple-template-id names a deducible template. [Note: A template template parameter (13.2 [temp.param]) does not name a deducible template. -- end note] [Note: An injected-class-name is never interpreted as a template-name in contexts where class template argument deduction would be performed (13.8.2 [temp.local]). —end note]

> (5) The other simple-type-specifiers specify either a previously-declared type, a type determined from an expression, or one of the fundamental types (6.9.2 [basic.fundamental]).

> (6)Table 17 summarizes the valid combinations of simple-type-specifier s and the types they specify.

#### 144. Position of `friend` specifier

Section: 9.2.9.5 [dcl.type.elab] Status: open Submitter: Daveed Vandevoorde Date: 22 Jul 1999

9.2.9.5 [dcl.type.elab] paragraph 1 seems to impose an ordering constraint on the elements of friend class declarations. However, the general rule is that declaration specifiers can appear in any order. Should

        class C friend;

be well-formed?

#### 1868. Meaning of “placeholder type”

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

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

#### 2671. friend named by a template-id

Section: 9.3.4.1 [dcl.meaning.general] Status: open Submitter: Davis Herring Date: 2022-11-18

Subclause 9.3.4.1 [dcl.meaning.general] paragraph 1 specifies:

> ... If the unqualified-id occurring in a declarator-id is a template-id, the declarator shall appear in the declaration of a template-declaration (13.7 [temp.decls]), explicit-specialization (13.9.4 [temp.expl.spec]), or explicit-instantiation (13.9.3 [temp.explicit]).

However, that is too restrictive and prevents befriending template specializations, for example:

      template<typename> void f();

      class A {
        friend void f<A>();
      };

#### 3030. Initializing array prvalues of unknown bound

Section: 9.3.4.5 [dcl.array] Status: open Submitter: Jim X Date: 2024-02-28

(From submission #509.)

Consider:

      int main(){
        using type = int[];
        type{1,2,3};
      }

Subclause 9.3.4.5 [dcl.array] paragraph 7 specifies:

> ... An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer (9.5 [dcl.init], 11.4 [class.mem], 7.6.1.4 [expr.type.conv], 7.6.2.8 [expr.new]). In these cases, the array bound is calculated from the number of initial elements (say, N) supplied (9.5.2 [dcl.init.aggr]), and the type of the array is “array of N U”.

However, there is no "declarator" in the example.

Possible resolution:

Change in 9.3.4.5 [dcl.array] paragraph 7 as follows:

> ... An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by with an initializer (9.5 [dcl.init], 11.4 [class.mem], 7.6.1.4 [expr.type.conv], 7.6.2.8 [expr.new]). In these cases, the array bound is calculated from the number of initial elements (say, N) supplied (9.5.2 [dcl.init.aggr]), and the type of the array is “array of N U”.

#### 1790. Ellipsis following function parameter pack

Section: 9.3.4.6 [dcl.fct] Status: open Submitter: Daryle Walker Date: 2013-10-01 Liaison: EWG

Although the current wording permits an ellipsis to immediately follow a function parameter pack, it is not clear that the `<cstdarg>` facilities permit access to the ellipsis arguments.

Rationale (June, 2014):

CWG felt that this is a question of language design and thus should be considered by EWG before any action.

EWG 2022-11-11

C23 removes the requirement that the last parameter be named for `va_start`. This is tracked in github issue cplusplus/papers#1374.

Additional notes (October, 2023)

Paper P2537 (Relax va_start Requirements to Match C) proposes to relax `va_start` to match C23, tracked via cplusplus/papers#1200.

#### 2802. Constrained `auto` and redeclaration with non-abbreviated syntax

Section: 9.3.4.6 [dcl.fct] Status: open Submitter: Richard Smith Date: 2023-09-26

Consider:

      template<typename T> void f(T);
      void f(auto);

These are redeclarations according to 9.3.4.6 [dcl.fct] paragraph 22:

> ... An abbreviated function template is equivalent to a function template (13.7.7 [temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. ...

The same applies to constrained abbreviated functions:

      template<C T> void g(T);
      void g(C auto);          // redeclaration

However, this leads to a situation where two function templates are equivalent, but not functionally equivalent:

      template<typename T> requires D<T> void h(C auto); // #1
      template<typename T, C U> requires D<T> void h(U); // #2

According to 13.5.3 [temp.constr.decl] bullet 3.3, #1 has the associated constraints `D<T> && C<auto_param_1>` whereas #2 has the associated constraints `C<U> && D<T>` (note reverse order). Those are observably different because satisfaction is checked in lexical order and instantiation of one of the associated constraints may yield an error not in the immediate context.

A number of options are available:

- Change 13.5.3 [temp.constr.decl] bullet 3.3 to check constraints from function parameters before those introduced by a requires-clause in the template-head.
- Do not defined the non-abbreviated form to be "equivalent" to the abbreviated function template in 9.3.4.6 [dcl.fct] paragraph 22. Redeclarations thus cannot use the other syntax.
- The "equivalent" only applies to non-constrained `auto` parameters.
- The "equivalent to" rule could append to the requires-clause in the template-head instead of inventing a constrained template parameter.

#### 2942. Packs in a function's parameter-type-list

Section: 9.3.4.6 [dcl.fct] Status: open Submitter: Corentin Jabot Date: 2024-10-25

(From submission #629.)

Subclause 9.3.4.6 [dcl.fct] paragraph 3 highlights a (singular) pack in a function's parameter-type-list:

> 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 parameter-declaration (9.3 [dcl.decl]). After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifier s 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.

The property of being a pack is part of the parameter type as determined by the parameter-declaration, plus there could be multiple packs involved.

Possible resolution

Change in 9.3.4.6 [dcl.fct] paragraph 3 as follows:

> 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 parameter-declaration (9.3 [dcl.decl]). After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifier s 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.

#### 325. When are default arguments parsed?

Section: 9.3.4.7 [dcl.fct.default] Status: open 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 [dcl.fct.default] paragraph 5 says "names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears"

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

      int b;
      class X {
        int mem2 (int i = b); // OK use X::b
        static int b;
      };

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

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

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

      class A {
        static int Foo (int i = Baz ());
        static int Baz (int i = Bar ());
        static int Bar (int i = 5);
     };

If that is well formed, at what point does the non-sensicalness of

      class B {
        static int Foo (int i = Baz ());
        static int Baz (int i = Foo());
      };

become detected? Is it when B is complete? Is it when B::Foo or B::Baz is called in such a way to require default argument expansion? Or is no diagnostic required?

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

      template <int A, typename B> struct T { static int i;};
      class C {
        int Foo (int i = T<1, int>::i);
      };

The default argument contains a non-parenthesized comma. Is it required that this comma is seen as part of the default argument expression and not the beginning of another of argument declaration? To accept this as part of the default argument would require name lookup of T (to determine that the '<' was part of a template argument list and not a less-than operator) before C is complete. Furthermore, the more pathological

      class D {
        int Foo (int i = T<1, int>::i);
        template <int A, typename B> struct T {static int i;};
      };

would be very hard to accept. Even though T is declared after Foo, T is in scope within Foo's default argument expression.

Suggested resolution:

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

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

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

Notes from the October, 2005 meeting:

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

Additional notes, May, 2009:

Presumably the following is ill-formed:

        int f(int = f());

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

Notes from the August, 2011 meeting:

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

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

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

Additional notes (August, 2011):

See also issues 1352 and 361.

Notes from the February, 2012 meeting:

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

Additional note (November, 2020):

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

#### 361. Forward reference to default argument

Section: 9.3.4.7 [dcl.fct.default] Status: open Submitter: Steve Clamage Date: 17 June 2002

Is this program well-formed?

      struct S {
        static int f2(int = f1()); // OK?
        static int f1(int = 2);
      };
      int main()
      {
        return S::f2();
      }

A class member function can in general refer to class members that are declared lexically later. But what about referring to default arguments of member functions that haven't yet been declared?

It seems to me that if f2 can refer to f1, it can also refer to the default argument of f1, but at least one compiler disagrees.

Notes from the February, 2012 meeting:

Implementations seem to have come to agreement that this example is ill-formed.

Additional note (March, 2013):

Additional discussion has occurred suggesting the following examples as illustrations of this issue:

      struct B {
        struct A { int a = 0; };
        B(A = A());    // Not permitted?
      };

as well as

      struct C {
       struct A { int a = C().n; }; // can we use the default argument here?
       C(int k = 0);
       int n;
      };

      bool f();
      struct D {
       struct A { bool a = noexcept(B()); }; // can we use the default initializer here?
       struct B { int b = f() ? throw 0 : 0; };
      };

(See also issue 325.)

Additional note (October, 2013):

Issue 1330 treats exception-specifications like default arguments, evaluated in the completed class type. That raises the same questions regarding self-referential `noexcept` clauses that apply to default arguments.

Additional note (November, 2020):

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

#### 1609. Default arguments and function parameter packs

Section: 9.3.4.7 [dcl.fct.default] Status: open Submitter: Jonathan Caves Date: 2013-01-25

It is not clear from 9.3.4.7 [dcl.fct.default] whether the following is well-formed or not:

      template<typename... T>
      void f2(int a = 0, T... b, int c = 1);

      f2<>(); // parameter a has the value 0 and parameter c has the value 1

(`T... b` is a non-deduced context per 13.10.3.6 [temp.deduct.type] paragraph 5, so the template arguments must be specified explicitly.)

Notes from the April, 2013 meeting:

CWG agreed that the example should be ill-formed.

Additional note (August, 2013):

9.3.4.7 [dcl.fct.default] paragraph 4 explicitly allows for a function parameter pack to follow a parameter with a default argument:

> In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack.

However, any instantiation of such a function template with a non-empty pack expansion would result in a function declaration in which one or more parameters without default arguments (from the pack expansion) would follow a parameter with a default argument and thus would be ill-formed. Such a function template declaration thus violates 13.8 [temp.res] paragraph 8:

> If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

Although the drafting review teleconference of 2013-08-26 suggested closing the issue as NAD, it is being kept open to discuss and resolve this apparent contradiction.

Notes from the September, 2013 meeting:

CWG agreed that this example should be accepted; the restriction on default arguments applies to the template declaration itself, not to its specializations.

#### 2701. Default arguments in multiple scopes / inheritance of array bounds in the same scope

Section: 9.3.4.7 [dcl.fct.default] Status: open Submitter: Richard Smith Date: 2020-11-20

It appears that P1787R6 has inadvertently changed the rules for where default arguments can be (re-)declared for a parameter. Consider:

    namespace N {
      void f(int a, int b = 1);
    }
    void N::f(int a = 1, int b) {} // valid before P1787R6, invalid afterwards
    void N::f(int a, int b = 2) {} // invalid before P1787R6, valid afterwards,
      // but this default argument will either never be used or will make any call using it ill-formed (unclear which)

Subclause 9.3.4.7 [dcl.fct.default] paragraph 4 specifies:

> For non-template functions, default arguments can be added in later declarations of a function that inhabit the same scope. Declarations that inhabit different scopes have completely distinct sets of default arguments. ...

The problem is that the out-of-line declaration of `N::f` inhabits the global namespace scope, not the scope of namespace `N`.

A similar problem exists for inheriting array bounds per 9.3.4.5 [dcl.array] paragraph 8:

> Furthermore, if there is a reachable declaration of the entity that inhabits the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.

Possible resolution:

1. Change in 6.4.1 [basic.scope.scope] paragraph 2 as follows:

   > ... The host scope of a declaration is the inhabited scope if that scope is a block scope and the target scope otherwise. An entity belongs to a scope S if S is the target scope of a declaration of the entity.

2. Change in 9.3.4.5 [dcl.array] paragraph 8 as follows:

   > Furthermore, if there is a reachable declaration of the entity that inhabits has the same host scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.

3. Change in 9.3.4.7 [dcl.fct.default] paragraph 4 as follows:

   > For non-template functions, default arguments can be added in later declarations of a function that inhabit have the same host scope. Declarations that inhabit have different host scopes have completely distinct sets of default arguments. ...

4. Change in 9.3.4.7 [dcl.fct.default] paragraph 9 as follows:

   > When an overload set contains a declaration of a function that inhabits a whose host scope is S, any default argument associated with any reachable declaration that inhabits whose host scope is S is available to the call.

5. Change in 12.2.4.1 [over.match.best.general] paragraph 4 as follows:

   > If the best viable function resolves to a function for which multiple declarations were found, and if any two of these declarations inhabit have different host scopes and specify a default argument that made the function viable, the program is ill-formed. [ Example: ...

   >       void use() {
   >        f(3);       // OK, default argument was not used for viability
   >        f();        // error: found default argument twice
   >       }

   >       int g(int, int = 0, int = 0);
   >       void h() {
   >         using ::g;
   >         int g(int, int, int = 1);
   >         int a = g(0); // OK, block scope declaration does not make the function viable;
   >                //uses default arguments of declaration whose host scope is the global namespace
   >       }

   > -- end example ]

Additional notes (March, 2025)

Reflector discussion pointed out that 12.2.4.1 [over.match.best.general] paragraph 4 confuses "overload candidates" (at most one per function) with "function declarations" (possibly several per function).

Option 1: Delete 12.2.4.1 [over.match.best.general] paragraph 4 and move its example to the end of 12.2.3 [over.match.viable] paragraph 2. Change in 12.2.3 [over.match.viable] bullet 2.3 as follows:

> A candidate function having more than m parameters is viable only if all parameters following the mth have default arguments ([dcl.fct.default]) introduced by a declaration of the function considered by overload resolution. If the candidate function is selected as the best viable function, there shall be a unique host scope in which such a function declaration declares a default argument for the (m+1)st parameter; the default arguments used in a call to the selected function are the default arguments introduced in that host scope. For the purposes of overload resolution, the parameter list is truncated on the right, so that there are exactly m parameters.

Option 2: Treat function declarations in distinct host scopes as different overload candidates if any default arguments would be used.

Option 3 (EWG): Disallow default arguments in extern "C" and block-scope declarations.

#### 3062. Overlapping specification of default template arguments

Section: 9.3.4.7 [dcl.fct.default] Status: open Submitter: Brian Bi Date: 2025-08-13

(From submission #750.)

As written, 9.3.4.7 [dcl.fct.default] applies to default arguments for constant template parameters, which are specified in 13.2 [temp.param] and 13.4.3 [temp.arg.nontype]. Remove the overlap.

Possible resolution:

1. Change in 9.3.4.7 [dcl.fct.default] paragraph 1 as follows:

   > If an initializer-clause is specified in a parameter-declaration other than a template-parameter (13.2 [temp.param]), this initializer-clause is used as a default argument. [Note 1: Default arguments will be used in calls where trailing arguments are missing (7.6.1.3 [expr.call]). —end note]

2. Change in 9.3.4.7 [dcl.fct.default] paragraph 3 as follows:

   > A default argument shall be specified only in the parameter-declaration-clause of a function declaration or lambda-declarator or in a template-parameter (13.2 [temp.param]). A default argument shall not be specified for a template parameter pack or a function parameter pack. If it is specified in a parameter-declaration-clause, it A default argument shall not occur within a declarator or abstract-declarator of a parameter-declaration. [ Footnote: .... ]

#### 2991. "array size" is vague

Section: 9.5.1 [dcl.init.general] Status: open Submitter: Jonathan Wakely Date: 2025-01-16

In 9.5.1 [dcl.init.general] bullet 16.5, the specification talks about "array size", but it is unclear whether that is the number of array elements or the size (in bytes) of the array.

Possible resolution:

Change in 9.5.1 [dcl.init.general] bullet 16.5 as follows:

> Otherwise, if the destination type is an array, the object is initialized as follows. The initializer shall be of the form ( expression-list ). Let x1 , . . . , xk be the elements of the expression-list. If the destination type is an array of unknown bound, it is defined as having k elements. Let n denote the number of elements of the array size after this potential adjustment. If k is greater than n, the program is ill-formed. Otherwise, ...

#### 2128. Imprecise rule for reference member initializer

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

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

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

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

> —end example]

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

- if the entity is a non-static data member that has a brace-or-equal-initializer and either

- the constructor's class is a union (11.5 [class.union]), and no other variant member of that union is designated by a mem-initializer-id or

- the constructor's class is not a union, and, if the entity is a member of an anonymous union, no other member of that union is designated by a mem-initializer-id,

the entity is initialized as specified in 9.5 [dcl.init], and the program is ill-formed if the entity is a reference member and this initialization binds it to a temporary;

#### 2704. Clarify meaning of "bind directly"

Section: 9.5.4 [dcl.init.ref] Status: open Submitter: Brian Bi Date: 2022-11-11

Currently, 9.5.4 [dcl.init.ref] paragraph 5 specifies:

> In all cases except the last (i.e., implicitly converting the initializer expression to the referenced type), the reference is said to bind directly to the initializer expression.

This is intended to refer to both sub-bullets of the last bullet (see issue 1604), but the presentation is unclear.

Suggested resolution:

1. Change in 9.5.4 [dcl.init.ref] bullet 5.4 as follows:

   > Otherwise, the reference is said to bind indirectly to the initializer expression:
   > - If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion (9.5 [dcl.init], 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered.
   > - Otherwise, the initializer expression is implicitly converted to a prvalue of type “T1”. The temporary materialization conversion is applied, considering the type of the prvalue to be “cv1 T1”, and the reference is bound to the result.
   > If T1 is reference-related to T2:
   > - cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and
   > - if the reference is an rvalue reference, the initializer expression shall not be an lvalue. [Note 3: This can be affected by whether the initializer expression is move-eligible (7.5.5.2 [expr.prim.id.unqual]). —end note]

2. Change in 9.5.4 [dcl.init.ref] paragraph 5 as follows:

   > In all cases except the last (i.e., implicitly converting the initializer expression to the referenced type), the The reference is said to bind directly to the initializer expression if it does not bind indirectly. [ Example:

   >       struct S {
   >         operator int();
   >       };
   >       struct T {
   >         T(int);
   >       };
   >       int&& r1 = S(); // binds directly
   >       T&& r2 = int(); // binds indirectly
   >       int&& r3 = double(); // binds indirectly

   > -- end example ]

#### 2168. Narrowing conversions and +/- infinity

Section: 9.5.5 [dcl.init.list] Status: open Submitter: Hubert Tong Date: 2015-08-19

The intended treatment of a floating point infinity with respect to narrowing conversions is not clear. Is `std::numeric_limits<double>::infinity()` usable in a constant expression, for example, and should that be different from a calculation that results in an infinity?

Notes from the October, 2015 meeting:

CWG requests the assistance of SG6 in resolving this issue.

Notes from the November, 2016 meeting:

SG6 said that arithmetic operations (not conversions) that produce infinity are not allowed in a constant expression. However, using `std::numeric_limits<T>::infinity()` is okay, but it can't be used as a subexpression. Conversions that produce infinity from non-infinity values are considered to be narrowing conversions.

#### 3023. Default arguments in list-initialization

Section: 9.5.5 [dcl.init.list] Status: open Submitter: Brian Bi Date: 2025-04-24

Consider:

      #include <iostream>
      struct I {
        I(int x) { std::cout << x; }
      };
      struct S {
        S(I, I = 2) {}
      };
      int main() {
        S(1, 2); std::cout << '\n';  // unspecified order; prints 12 or 21
        S{1, 2}; std::cout << '\n';  // prints 12 (9.5.5 [dcl.init.list] paragraph 4)
        S{1};                        // ???
      }

Since no initializer-clause is present for the second argument, 9.5.5 [dcl.init.list] paragraph 4 does not, but should, prescribe lexical ordering of the argument evaluations.

Possible resolution:

Change in 9.5.5 [dcl.init.list] paragraph 4 as follows:

> Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (13.7.4 [temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. If the elements of the initializer-list are interpreted as arguments of a constructor call (12.2.2.8 [over.match.list]), any default arguments are evaluated in the order as-if an initializer-clause were present for the corresponding parameter.

> [Note 4: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. [ Example:

>       #include <iostream>
>       struct I {
>         I(int x) { std::cout << x; }
>       };
>       struct S {
>         S(I, I = 2) {}
>       };
>       int main() {
>         S(1, 2); std::cout << '\n';  // unspecified order; prints 12 or 21
>         S{1, 2}; std::cout << '\n';  // prints 12
>         S{1};                        // prints 12
>       }

> -- end example ] —end note]

#### 1962. Type of `__func__`

Section: 9.6.1 [dcl.fct.def.general] Status: open Submitter: Steve Clamage Date: 2014-07-04 Liaison: EWG

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

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

even though `T` is specified to be an array type.

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

      inline auto f() { return &__func__; }

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

Notes from the May, 2015 meeting:

CWG agreed with the suggested direction.

Rationale (November, 2018):

See also issue 2362, which asks for the ability to use `__func__` in a constexpr function. These two goals are incompatible, so EWG input is requested.

EWG 2022-11-11

Paper requested. This is tracked in github issue cplusplus/papers#1375.

#### 2362. `__func__` should be `constexpr`

Section: 9.6.1 [dcl.fct.def.general] Status: open Submitter: Anthony Polukhin Date: 2017-10-23 Liaison: EWG

The definition of `__func__` in 9.6.1 [dcl.fct.def.general] paragraph 8 is:

      static const char __func__[] = "function-name";

This prohibits its use in constant expressions, e.g.,

      int main () {
        // error: the value of __func__ is not usable in a constant expression
        constexpr char c = __func__[0];
      }

Notes from the October, 2018 teleconference:

CWG agreed with the proposed change.

Rationale (November, 2018):

See also issue 1962, which asks that the type of `__func__` be `const char*`. These two goals are incompatible, so EWG input is requested.

EWG 2022-11-11

This is tracked in github issue cplusplus/papers#1378.

#### 2993. Body of a destructor

Section: 9.6.1 [dcl.fct.def.general] Status: open Submitter: Hubert Tong Date: 2025-02-14

(From submission #672.)

Subclause 9.6.1 [dcl.fct.def.general] paragraph 1 specifies additional implicit actions performed by the body of a constructor, but neglects destructors.

Possible resolution:

Change in 9.6.1 [dcl.fct.def.general] 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, for For a constructor, this includes 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]). For a destructor, this includes invocations of destructors for base and member subobjects. The optional attribute-specifier-seq in a function-definition appertains to the function. A function-definition with a virt-specifier-seq shall be a member-declaration (11.4 [class.mem]). A function-definition with a requires-clause shall define a templated function.

#### 2997. Defaulted functions with deleted definition

Section: 9.6.2 [dcl.fct.def.default] Status: open Submitter: Brian Bi Date: 2023-08-02

When a function is defined as defaulted, the defaulted definition might be deleted. The standard requires that a non-user-provided defaulted function not be defined until it is odr-used or needed for constant evaluation (9.6.2 [dcl.fct.def.default] paragraph 5).

Consider:

      template <class T>
      struct X {
        static_assert(sizeof(T) > 1);
      };

      template <class T>
      struct Y {
        bool operator==(const Y& other) const {
          return true;
        }
      };

      struct Z {
        Y<X<char>> y;
        bool operator==(const Z& other) const = default;
      };

      int main() {
        // Z z;
        // z == z
      }

In order to determine whether the equality operator of `Z` is deleted, it is necessary to perform argument-dependent lookup for the equality operator that applies to `Y<X<char>>`, causing instantiation of the ill-formed definition of `X<char>`.

Clang, EDG, and MSVC perform the check at the end of the class-specifier for `Z` , making the program ill-formed. In contrast, gcc performs the check later, if/when the equality operator is actually used.

Whether a defaulted special member function (e.g. the copy constructor, not the equality operator function) is deleted can make a difference for class properties such as trivial copyability, affected calling conventions and thus requiring early checking. Is it worthwhile to introduce an inconsistency for the equality operator here? On the other hand, special member functions cannot cause argument-dependent lookup for their default definitions, so the problem in this issue does not arise in the first place.

A closely related question is how to interpret 13.9.2 [temp.inst] paragraph 3. Implicit instantiation of the definition of a class template specialization instantiates the definition of a member function only if the member function is deleted. Does that mean, at the point of instantiation of the class, the compiler must determine whether a defaulted definition is deleted?

The issue also arises when attempting to default equality operator functions for standard library types, e.g.

      template<class T, int N>
      struct A {
        T m[N];
        bool operator==(const A&) const = default;
      };

      struct Incomplete;
      template<class T> struct Holder { T t; };
      using H = Holder<Holder<Incomplete>*>;
      A<H, 2> a;    // error: data member of incomplete type

#### 2562. Exceptions thrown during coroutine startup

Section: 9.6.4 [dcl.fct.def.coroutine] Status: open Submitter: Tomasz Kamiński Date: 2022-04-06

Subclause 9.6.4 [dcl.fct.def.coroutine] seems to miss specification about the behavior of coroutines when an exception is thrown during the early phases of a coroutine evaluation. It is unclear at what point the ownership of the coroutine frame is passed from the compiler to the user, who then needs to call `std::coroutine_handle::destroy` to destroy and free the coroutine frame, including the parameter copies. The following situations arise:

- Allocation fails, and `get_return_object_on_allocation_failure()` is returned.
- Allocation succeeds, but an exception is thrown before the return object is initialized (e.g. when initializing the promise or evaluating `get_return_object`). The coroutine state ought to be deallocated automatically and the exception propagated.
- The return object is successfully initialized, but an exception would leave the coroutine before it would first suspend, either because initial-suspend's `await_ready` or `await_suspend` exits via an exception, or because `unhandled_exception` rethrows. The coroutine ought to be suspended at its final suspend point, the return object destroyed, and the exception propagated; the return object's destructor is responsible for ensuring the coroutine is destroyed.
- The coroutine ever suspends, or the coroutine terminates normally without ever suspending. The return object is returned to the caller, carrying with it ownership of the coroutine state.

See also issue 2451.

#### 2934. Unclear semantics of exception escaping from `unhandled_exception`

Section: 9.6.4 [dcl.fct.def.coroutine] Status: open Submitter: Brian Bi Date: 2024-07-13

(From submission #574. See also coroutines-ts#17 and coroutines-ts#30.)

Subclause 9.6.4 [dcl.fct.def.coroutine] paragraph 14 specifies:

> If the evaluation of the expression `promise.unhandled_exception()` exits via an exception, the coroutine is considered suspended at the final suspend point and the exception propagates to the caller or resumer.

However, implementations destroy the promise object, the parameter copies, and the coroutine state before propagating the exception, which is consistent with the as-if code presented in 9.6.4 [dcl.fct.def.coroutine] paragraph 5.

CWG 2024-11-08

The implementations are correct; the normative wording needs to be amended.

#### 2935. Destroying the coroutine state when initial-await-resume-called is false

Section: 9.6.4 [dcl.fct.def.coroutine] Status: open Submitter: Brian Bi Date: 2024-07-13

(From submission #575.)

According to 9.6.4 [dcl.fct.def.coroutine] paragraph 11:

> The coroutine state is destroyed when control flows off the end of the coroutine or the destroy member function (17.13.4.6 [coroutine.handle.resumption]) of a coroutine handle (17.13.4 [coroutine.handle]) that refers to the coroutine is invoked.

Considering the as-if code in 9.6.4 [dcl.fct.def.coroutine] paragraph 5, if an exception occurs at any point before initial-await-resume-called becomes `true`, then there is no requirement to destroy the coroutine state, nor is the coroutine ever suspended. Consequently, the coroutine state is necessarily leaked. It is unclear whether that is intentional.

CWG 2024-11-08

It is unclear what the intended outcome is.

#### 3040. Mishandling of lambda coroutines

Section: 9.6.4 [dcl.fct.def.coroutine] Status: open Submitter: Jiang An Date: 2025-07-03

(From submission #721.)

Subclause 9.6.4 [dcl.fct.def.coroutine] paragraph 3 and paragraph 4 do not properly handle function call operators of lambdas that are coroutines. In particular, `*this` does not denote the lambda's closure object and a captureless lambda should be treated like a static member function, because the result of a captureless lambda's conversion function does not take a pointer to the closure object.

Possible resolution:

1. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 3 as follows:

   > The promise type of a coroutine is `std::coroutine_traits<R, P1 , . . . , Pn >::promise_type`, where R is the return type of the function, and P1 . . . Pn is the sequence of types of the non-object function parameters, preceded by the type of the object parameter (9.3.4.6 [dcl.fct]) if the coroutine is a non-static member function that is not a member of the closure type for a lambda-expression with no lambda-capture and no explicit object parameter (7.5.6.2 [expr.prim.lambda.closure]). The promise type shall be a class type.

2. Change in 9.6.4 [dcl.fct.def.coroutine] paragraph 4 as follows:

   > A this-eligible function is an implicit object member function that is not a member of the closure type for a lambda-expression with no lambda-capture. 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 an implicit object member this-eligible function, and pi denotes the ith function parameter otherwise. For an implicit object member this-eligible function, q1 is an lvalue that denotes `*this` or, if the function is a member of a closure type, the closure object; any other qi is an lvalue that denotes the parameter copy corresponding to pi , as described below.

#### 2340. Reference collapsing and structured bindings

Section: 9.7 [dcl.struct.bind] Status: open Submitter: Daveed Vandevoorde Date: 2017-03-29

According to 9.7 [dcl.struct.bind] paragraph 3,

> Given the type `T` i designated by `std::tuple_element<i, E>::type`, each `v` i is a variable of type “reference to `T` i” initialized with the initializer, where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise; the referenced type is `T` i.

If `T` i is already a reference type, should this do reference collapsing? Presumably yes, but reference collapsing is specified in terms of a typedef-name or decltype-specifier, which are not used in this description.

See also issue 2313.

#### 3046. Enumerations as part of the common initial sequence

Section: 9.8.1 [dcl.enum] Status: open Submitter: Benjamin Sch. Date: 2025-07-28

(From submission #732.)

Consider:

      #include <iostream>
      #include <type_traits>

      enum E { E0 } e;
      enum F { F0, F1, F2, F3 };
      static_assert(std::is_same_v<std::underlying_type_t<E>, std::underlying_type_t<F>>);  // assume this passes

      struct A { E e; };
      struct B { F f; };
      union U {
        A a;
        B b;
      } u;

      bool test() {
        return u.a.e == 2;
      }

      auto ptest = test;

      int main() {
        u.b.f = F2;
        std::cout << ptest();
      }

Both `u.a` and `u.b` are part of the common initial sequence, allowing `u.a.e` to read the value of `u.a.f`, even though `E` cannot represent all values of `F`.

Possible resolution:

Change in 6.9.1 [basic.types.general] paragraph 10 as follows:

> Two enumeration types are layout-compatible enumerations if they have the same underlying type values.

#### 813. `typename` in a using-declaration with a non-dependent name

Section: 9.10 [namespace.udecl] Status: open Submitter: UK Date: 3 March, 2009
N2800 comment UK 101

9.10 [namespace.udecl] paragraph 20 says,

> If a using-declaration uses the keyword `typename` and specifies a dependent name (13.8.3 [temp.dep]), the name introduced by the using-declaration is treated as a typedef-name (9.2.4 [dcl.typedef]).

This wording does not address use of `typename` in a using-declaration with a non-dependent name; the primary specification of the `typename` keyword in 13.8 [temp.res] does not appear to describe this case, either.

Additional notes (March, 2022):

The relevant wording is now in 13.8.1 [temp.res.general] paragraph 5:

> A name that refers to a using-declarator whose terminal name is dependent is interpreted as a typedef-name if the using-declarator uses the keyword `typename`.

#### 3028. A using-declarator should bind a name

Section: 9.10 [namespace.udecl] Status: open Submitter: Brian Bi Date: 2025-03-24

(From submission #692.)

There is no normative rule that would cause a using-declarator not naming a constructor to bind its unqualified-id in the scope it inhabits.

Also, a normative prohibition for something like `using X::~X;` seems to be missing.

Suggested resolution:

1. Change the grammar before 9.10 [namespace.udecl] paragraph 1 as follows:

         using-declarator:
           typenameopt nested-name-specifier unqualified-id using-declarator-id

         using-declarator-id:
           identifier
           operator-function-id
           conversion-function-id
           literal-operator-id

2. Change in 9.10 [namespace.udecl] paragraph 1 as follows:

   > The component names of a using-declarator are those of its nested-name-specifier and unqualified-id using-declarator-id. The component name of a using-declarator-id is itself. Each using-declarator in a using-declaration [ Footnote: ... ] names the set of declarations found by lookup (6.5.5 [basic.lookup.qual]) for the using-declarator, except that class and enumeration declarations that would be discarded are merely ignored when checking for ambiguity (6.5 [basic.lookup]), conversion function templates with a dependent return type are ignored, and certain functions are hidden as described below. If the terminal name of the using-declarator is dependent (13.8.3.2 [temp.dep.type]), the using-declarator is considered to name a constructor if and only if the using-declarator-id is an identifier and the nested-name-specifier has a terminal name that is the same as the unqualified-id identifier. If the lookup in any instantiation finds that a using-declarator that is not considered to name a constructor does do so, or that a using-declarator that is considered to name a constructor does not, the program is ill-formed.

3. Change in 9.10 [namespace.udecl] paragraph 2 as follows:

   > If the using-declarator names a constructor, it declares that the class inherits the named set of constructor declarations from the nominated base class. Otherwise, the using-declarator introduces its using-declarator-id. [Note 1: Otherwise, the unqualified-id in the using-declarator is bound to During name lookup, the using-declarator, which is replaced during name lookup with the declarations it names (6.5 [basic.lookup]). If such a declaration is of an enumeration, the names of its enumerators are not bound. For the keyword typename, see 13.8 [temp.res]. —end note]

4. Change in 9.10 [namespace.udecl] paragraph 4 as follows:

   > [Note 2: Since destructors do not have names, a using-declaration cannot refer to a destructor for a base class. —end note] If a constructor or assignment operator brought from a base class into a derived class ...

5. Remove 9.10 [namespace.udecl] paragraph 5:

   > A using-declaration shall not name a template-id.

   >       struct A {
   >         template <class T> void f(T);
   >         template <class T> struct X { };
   >       };
   >       struct B : A {
   >         using A::f<double>; // error
   >         using A::X<int>;    // error
   >       };

#### 3037. Name lookup results for using-declarators

Section: 9.10 [namespace.udecl] Status: open Submitter: Vlad Serebrennikov Date: 2025-06-10

Subclause 9.10 [namespace.udecl] paragraph 1 subtly changes the mechanics of name lookup for a using-declarator. This is better done in 6.5.1 [basic.lookup.general] to avoid the impression of conflicting rules.

Possible resolution:

1. Change in 6.5.1 [basic.lookup.general] paragraph 1 as follows:

   > ... If the declarations found by name lookup all denote functions or function templates, the declarations are said to form an overload set. Otherwise, if the declarations found by name lookup do not all denote the same entity, they are ambiguous and the program is ill-formed, unless lookup is for a using-declarator. ...

2. Change in 6.5.1 [basic.lookup.general] paragraph 4 as follows:

   > In certain contexts, only certain kinds of declarations are included in the result of the search. After any applying such restriction restrictions (if any), any declarations of classes or enumerations are discarded if lookup is not for a using-declarator and any other declarations are found. [Note 4: A type (but not a type alias or template) is therefore hidden by any other entity in its scope. —end note] However, if a lookup is type-only, only declarations of types and templates whose specializations are types are considered; furthermore, if declarations of a type alias and of its underlying entity are found, the declaration of the type alias is discarded instead of the type declaration.

3. Change in 9.10 [namespace.udecl] paragraph 1 as follows:

   > Each using-declarator in a using-declaration [ Footnote: ... ] names the set of declarations found by lookup (6.5.5 [basic.lookup.qual]) for the using-declarator, except that class and enumeration declarations that would be discarded are merely ignored when checking for ambiguity (6.5 [basic.lookup]), conversion function templates with a dependent return type are ignored, and certain functions are hidden as described below.

#### 1817. Linkage specifications and nested scopes

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

According to 9.1 [dcl.pre] paragraph 2,

> Unless otherwise stated, utterances in Clause 9 [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.12 [dcl.link] paragraph 4, which says,

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

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

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

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

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

Proposed resolution (February, 2014):

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

> Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope (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:

> ...

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

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

Additional note, November, 2014:

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

#### 2866. Observing the effects of `[[no_unique_address]]`

Section: 9.13 [dcl.attr] Status: open Submitter: Brian Bi Date: 2023-11-12 Liaison: EWG

Some attributes can cause undefined behavior, e.g. `[[no_unique_address]]` via attempting to transparently replace a subobject. Core language undefined behavior can be detected during constant evaluation (7.7 [expr.const] bullet 5.8). For example:

      struct A {
        [[no_unique_address]] int x;
      };
      constexpr int test(auto a) {
        std::construct_at(&a.x, 1);   // #1
        return a.x;
      }
      template<typename T> constexpr bool call(int (*)[test(T())]) { return false; }
      template<typename T> constexpr bool call(...) { return true; }
      bool no_unique_address_works = call<A>(nullptr);

If `[[no_unique_address]]` has an effect, the attempt to transparently replace the member `A::x` at #1 is undefined behavior per 6.8.4 [basic.life] bullet 8.4, otherwise the replacement succeeds.

This behavior conflicts with the note in 9.13.1 [dcl.attr.grammar] paragraph 6:

> [Note 5: The attributes specified in 9.13 [dcl.attr] have optional semantics: given a well-formed program, removing all instances of any one of those attributes results in a program whose set of possible executions (4.1.2 [intro.abstract]) for a given input is a subset of those of the original program for the same input, absent implementation-defined guarantees with respect to that attribute. —end note]

See also issue 2545 for implementation divergence and implementation misbehavior when replacing potentially-overlapping subobjects in constant expressions.

See also issue 2403, which might be resolved to demonstrate different behavior for potentially-overlapping subobjects (always copies) vs. non-overlapping subobjects (never copies; guaranteed copy elision).

CWG 2024-04-19

The note embodies EWG design guidelines for attributes; CWG requests EWG to address the inconsistency, via paper issue 1890.

#### 3038. Ignorability of attributes, again

Section: 9.13.1 [dcl.attr.grammar] Status: open Submitter: Jiang An Date: 2025-06-17 Liaison: EWG

(From submission #716.)

The following note added to 9.13.1 [dcl.attr.grammar] by issue 2695 embodies the design intent that attributes can be ignored by implementations:

> [Note 5: The attributes specified in 9.13 [dcl.attr] have optional semantics: given a well-formed program, removing all instances of any one of those attributes results in a program whose set of possible executions (4.1.2 [intro.abstract]) for a given input is a subset of those of the original program for the same input, absent implementation-defined guarantees with respect to that attribute. -- end note]

However, the note is incorrect for attributes that contain a potentially-evaluated expression such as `[[assume]]` (9.13.3 [dcl.attr.assume]). For example,

      template<int N>
      struct reader {
        friend auto flag(reader);
      };

      template<int N>
      struct setter {
        friend auto flag(reader<N>) {}
      };

      template<int N = 0, auto DifferenceMaker = [] {}>
      consteval int next() {
        if constexpr (requires { flag(reader<N>{}); }) {
         return next<N + 1>();
       } else {
         (void) setter<N>{};
         return N;
       }
      }

      int main() {
        static_assert(next() == 0);
        [[assume(true || next())]];
        static_assert(next() == 2);
      }

While this particular example using stateful friend injection is addressed by issue 2118, similar examples can be constructed using mainstream facilities introduced by P2996R13 (Reflection); see the compile-time ticket counter example therein.

There are also counterexamples for `[[no_unique_address]]`.

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

#### 1617. `alignas` and non-defining declarations

Section: 9.13.2 [dcl.align] Status: open Submitter: Richard Smith Date: 2012-02-02

According to 9.13.2 [dcl.align] paragraph 6,

> If the defining declaration of an entity has an alignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have no alignment-specifier. Conversely, if any declaration of an entity has an alignment-specifier, every defining declaration of that entity shall specify an equivalent alignment. No diagnostic is required if declarations of an entity have different alignment-specifiers in different translation units.

Because this is phrased in terms of the definition of an entity, an example like the following is presumably well-formed (even though there can be no definition of `n`):

       alignas(8) extern int n;
       alignas(16) extern int n;

Is this intentional?

#### 3024. Alignment of references

Section: 9.13.2 [dcl.align] Status: open Submitter: Brian Bi Date: 2025-05-02

(From submission #703.)

Per 9.13.2 [dcl.align] paragraph 1, an alignment-specifier may be applied to a reference. However, it is unclear what "alignment requirements for a reference" actually means. There is implementation divergence

Suggested resolution:

Change in 9.13.2 [dcl.align] paragraph 1 as follows:

> An alignment-specifier may be applied to a variable or to a class non-static data member, but it shall not be applied to a reference, a bit-field, a function parameter, or an exception-declaration (14.4 [except.handle]). An alignment-specifier may also be applied to the declaration of a class (in an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]) or class-head (Clause 11 [class] 11.1 [class.pre]), respectively). An alignment-specifier with an ellipsis is a pack expansion (13.7.4 [temp.variadic]).

#### 3050. [[deprecated]] for class template partial specializations

Section: 9.13.4 [dcl.attr.deprecated] Status: open Submitter: Jonathan Caves Date: 2025-08-12

Consider:

      template<typename T>
      struct S {
        static constexpr bool value = false;
      };

      template<typename T>
      struct [[deprecated]] S<const T> {
        static constexpr bool value = true;
      };

      static_assert(S<const int>::value, "BOOM!");

Should this trigger the deprecation warning for using the partial specialization? There is implementation divergence.

One consistent approach (used by clang) is to instantiate a declaration of the specialization from the primary template, but to instantiate the definition from the applicable partial specialization. Thus, `[[deprecate]]` on a partial specialization comes into effect when its definition is instantiated. However, C++ has some situations where a definition is surprisingly required, e.g.:

      void f(...);

      template<typename T>
      struct S {
        static constexpr bool value = false;
      };

      template<typename T>
      struct [[deprecated]] S<const T> {
        static constexpr bool value = true;
      };

      S<const int> *p;
      void g() { f(p); }

#### 2541. Linkage specifications, module purview, and module attachment

Section: 10.1 [module.unit] Status: open Submitter: Nathan Sidwell Date: 2022-02-28

The interaction between linkage specifications (9.12 [dcl.link]) and named or global module purview and attachment (10.1 [module.unit]) is confusing. The addition of linkage declarations attaching their contents to the global module is not fully integrated into the wording and examples would also help.

- Module purview and attachment are not necessarily the same. This is never explicitly mentioned.
- 10.1 [module.unit] paragraph 7 explicitly calls out replaceable allocation and deallocation functions as being (implicitly) attached to the global module. This is unnecessary.
- 6.10.3.1 [basic.start.main] paragraph 1 specifies that there shall be a main function in the global namespace, but only in paragraph 3 do we specify that not attaching it to the global module is ill-formed.

Suggested resolution:

1. Change 6.10.3.1 [basic.start.main] paragraph 1 as follows:

   > A program shall contain exactly one function called `main` that belongs to the global scope and is attached to the global module. Executing a program starts a main thread of execution (6.10.2 [intro.multithread], 32.4 [thread.threads]) in which the `main` function is invoked. It is implementation-defined whether a program in a freestanding environment is required to define a `main` function.

2. Change 10.1 [module.unit] bullet 7.2 as follows:

   > - ...
   > - Otherwise, if the declaration
   >   - is a replaceable global allocation or deallocation function (17.6.3.2 [new.delete.single], 17.6.3.3 [new.delete.array]), or
   >   - is a namespace-definition with external linkage, or
   >   - appears within a linkage-specification,
   >   it is attached to the global module.
   > - Otherwise, ...

3. Add an example at the end of 10.1 [module.unit] paragraph 7:

   > [ Example:

   >       // Translation unit #1
   >       export module Foo;
   >       void f();              // module linkage, attached to named module Foo
   >       extern "C++" {
   >         export void g();     // nameable by importers
   >         void h();            // nameable in Foo's purview
   >       }

   > Both `g` and `h` have external linkage, are attached to the global module, and can thus also be redeclared in other translation units:

   >       // Legacy header "foo.h"
   >       extern "C++" void g();

   >       // Legacy header "foo-internal.h"
   >       extern "C++" void h();

   > -- end example ]

   > A module-declaration that contains neither...

4. Change in 10.2 [module.interface] paragraph 6 as follows:

   > A redeclaration of an entity X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported unless it has external linkage..

#### 2727. Importing header units synthesized from source files

Section: 10.3 [module.import] Status: open Submitter: Jim X Date: 2023-05-04

Subclause 10.3 [module.import] paragraph 5 specifies:

> A module-import-declaration that specifies a header-name H imports a synthesized header unit, which is a translation unit formed by applying phases 1 to 7 of translation (5.2 [lex.phases]) to the source file or header nominated by H, which shall not contain a module-declaration. ... An importable header is a member of an implementation-defined set of headers that includes all importable C++ library headers (16.4.2.3 [headers]). H shall identify an importable header. ...

This text seems to recognize that header units may be synthesized from non-headers (i.e. source files), yet a module-import-declaration of the form `import header-name` is required to name an (importable) header, not an arbitrary source file turned into header unit.

Possible resolution:

Change in 10.3 [module.import] paragraph 5 as follows:

> H shall identify an importable header. If H nominates a header, that header shall be an importable header.

#### 2971. Specializations for a class are not decl-reachable

Section: 10.4 [module.global.frag] Status: open Submitter: Davis Herring Date: 2024-12-19

(From submission #655.)

Consider:

      module;
      // included...
      template<int> struct X {};
      template<> struct X<0> {};
      struct Z;
      template<class> struct Y {};
      template<> struct Y<Z> {};
      // ...done
      export module M;
      export using ::X;
      export using ::Z;

Subclause 10.4 [module.global.frag] bullet 3.5.7 implies that `X<0>`, but not `Y<Z>` is reachable in importers. This is not helpful when `Y` is a trait defined by another library and specialized for the library being exposed as `M`.

Possible resolution:

Change in 10.4 [module.global.frag] bullet 3.5 as follows:

> A declaration D is decl-reachable from a declaration S in the same translation unit if
> - ...
> - there exists a declaration M that is not a namespace-definition for which M is decl-reachable from S and either
>   - D is decl-reachable from M , or
>   - D and M declare the same entity, and D neither is a friend declaration nor inhabits a block scope, or
>   - D declares a namespace N and M is a member of N , or
>   - one of D and M declares a class or class template C and the other declares a member or friend of C, or
>   - one of D and M declares an enumeration E and the other declares an enumerator of E, or
>   - D declares a function or variable and M is declared in D, [ Footnote: ... ] or
>   - one of D and M declares a template and the other declares a partial or explicit specialization or an implicit or explicit instantiation of that template, or
>   - D declares a partial or explicit specialization or an explicit instantiation and M declares an entity that appears in D's (possibly deduced) template arguments, or
>   - M declares a class template and D is a deduction guide for that template, or
>   - one of D and M declares a class or enumeration type and the other introduces a typedef name for linkage purposes for that type.

#### 511. POD-structs with template assignment operators

Section: 11.2 [class.prop] Status: open Submitter: Alisdair Meredith Date: 19 Mar 2005

A POD-struct is not permitted to have a user-declared copy assignment operator (11.2 [class.prop] paragraph 1). However, a template assignment operator is not considered a copy assignment operator, even though its specializations can be selected by overload resolution for performing copy operations (11.4.6 [class.copy.assign] paragraph 12). Consequently, `X` in the following code is a POD, notwithstanding the fact that copy assignment (for a non-const operand) is a member function call rather than a bitwise copy:

        struct X {
          template<typename T> const X& operator=(T&);
        };
        void f() {
          X x1, x2;
          x1 = x2;  // calls X::operator=<X>(X&)
        }

Is this intentional?

#### 2463. Trivial copyability and unions with non-trivial members

Section: 11.2 [class.prop] Status: open Submitter: Daveed Vandevoorde Date: 2020-11-30 Liaison: EWG

According to 11.2 [class.prop] paragraph 1,

> A trivially copyable class is a class:

> - that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator (11.4.4 [special], 11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]),

> - where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and

> - that has a trivial, non-deleted destructor (11.4.7 [class.dtor]).

This definition has surprising effects in a union whose members are not trivial. For example:

      struct S {
        S& operator=(const S&);
      };
      union U {
        S s;
      };

In this case, `S` is not trivially copyable because its assignment operator is non-trivial, although its copy constructor is trivial. `U`, however, is trivially copyable because its assignment operator is not eligible (11.4.4 [special] paragraph 6) because it is deleted, but its copy constructor is trivial, thus satisfying the second bullet.

It is unclear why, for example, a complete object of type `S` cannot be `memcpy`ed but such an object can be `memcpy`ed when embedded in a union.

There is implementation divergence in the handling of this example.

CWG 2022-11-10

Traditionally, the rule for trivial copyability has been that each of the potentially user-written ways of copying a class (copy/move constructors, copy/move assignment operators) have to be trivial (or deleted). See C++17 subclause 12p6:

> A trivially copyable class is a class:
> - where each copy constructor, move constructor, copy assignment operator, and move assignment operator (15.8, 16.5.3) is either deleted or trivial,
> - that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and
> - that has a trivial, non-deleted destructor (15.4).

That seems unhelpful. The rule should instead be that if there is any way of copying the class such that the compiler will generate a `memcpy` (because the corresponding operation is trivial), the user should be allowed to perform `memcpy`, too. In terms of wording, this amounts to striking the first bullet and adding "trivial" to the second bullet. (The wording in the current working draft considers eligibility, which complicates the treatment slightly in terms unrelated to the present issue.)

CWG is seeking EWG advice on this issue via cplusplus/papers#1363.

Additional notes (March, 2023)

`std::tuple` with trivially-copyable element types and with no elements (`std::tuple<>`) ought to be trivially copyable, but the recent addition of `const`-qualified assignment operators makes that not so under the status quo core language rules.

#### 2736. Standard layout class with empty base class also in first member

Section: 11.2 [class.prop] Status: open Submitter: Lénárd Szolnoki Date: 2023-05-10

(From submission #317.)

Consider:

      struct A {};
      struct B : A { char c; };

      struct C : A {
        B b;
      };

Class C satisfies all the conditions to be considered standard-layout, including 11.2 [class.prop] bullet 3.7:

- M(C) is the union of {B} and M(B) per 11.2 [class.prop] bullet 3.7.2.
- M(B) is the union of {char} and M(char) per 11.2 [class.prop] bullet 3.7.2.
- M(char) is an empty set per 11.2 [class.prop] bullet 3.7.5.

Thus, M(C) is {char,B}, neither of which is a base class of C, and thus C is standard-layout, contrary to popular ABI rules that do not provide pointer interconvertibility between C::b and C.

Suggested resolution (reviewed by CWG 2024-03-01, with no consensus):

1. Change in 11.2 [class.prop] bullet 3.7 as follows:

   > - ...
   > - has no element of the set M(S) of types as a base class, where for any type X, M(X) is defined as follows, where `B(S)` is a set of types consisting of `S` and each direct and indirect base class of `S`. [Footnote: ...] [Note 2: M(X) is the set of the types of all non-base-class subobjects that can be at a zero offset in X. —end note]
   >   - If X is a non-union class type with no non-static data members, the set M(X) is empty.
   >   - If X is a non-union class type with a non-static data member of type X0 that is either of zero size or is the first non-static data member of X (where said member may be an anonymous union), the set M(X) consists of X0 is the union of B(X0) and the elements of M(X0).
   >   - If X is a union type, the set M(X) is the union of all M(Ui) and the set containing all Ui B(Ui), where each Ui is the type of the ith non-static data member of X.
   >   - If X is an array type with element type Xe, the set M(X) consists of Xe is the union of B(Xe) and the elements of M(Xe).
   >   - If X is a non-class, non-array type, the set M(X) is empty.

2. Add more examples in 11.2 [class.prop] paragraph 4:

         struct X {};                     // standard-layout class
         struct Y : X { char c; };        // standard-layout class
         struct Z : X { Y y; };           // not a standard-layout class

         struct H {};                     // standard-layout class
         struct I : H {};                 // standard-layout class
         struct J { I i; };               // standard-layout class
         struct K : I { J j; };           // not a standard-layout class

#### 3049. Implicitly deleted move operation should not disable trivial relocation

Section: 11.2 [class.prop] Status: open Submitter: Pablo Halpern Date: 2025-07-10 Liaison: EWG

Consider:

      struct A { const int i; };   // trivially copyable, but not trivially relocatable (deleted copy-assignment)
      struct B { int& r; };        // trivially copyable, but not trivially relocatable (deleted copy-assignment)
      struct C trivially_relocatable_if_eligible { C(); C operator=(const C&) = delete; }; // trivially relocatable
      struct D { C m; };           // not trivially relocatable

All of `A`, `B`, `D` ought to be trivially relocatable, but are not per the current Working Draft, because their copy assignment operators are implicitly deleted.

Suggested resolution:

Change in 11.2 [class.prop] paragraph 2 as follows:

> A class C is default-movable if
> - overload resolution for direct-initializing an object of type C from an xvalue of type C selects a constructor that is a direct member of C and is neither user-provided nor explicitly deleted,
> - overload resolution for assigning to an lvalue of type C from an xvalue of type C selects an assignment operator function that is a direct member of C and is neither user-provided nor explicitly deleted, and
> - C has a destructor that is neither user-provided nor explicitly deleted.

Additional notes (August, 2025)

Forwarded to EWG to confirm design intent with paper issue #2408, by decision of the CWG chair.

#### 2188. empty-declaration grammar ambiguity

Section: 11.4.1 [class.mem.general] Status: open Submitter: Jens Maurer Date: 2015-10-21

(The originally reported ambiguity between simple-declaration and empty-declaration does not seem to exist.)

There is a grammar ambiguity between

      empty-declaration:
         ;

and

      member-declaration :
          attribute-specifier-seqopt decl-specifier-seqopt member-declarator-listopt ;
          ...
          empty-declaration

#### 2852. Complete-class contexts and class-scope lambdas

Section: 11.4.1 [class.mem.general] Status: open Submitter: Richard Smith Date: 2023-10-25

(From submission #449 and gcc bugzilla 111923.)

Subclause 11.5.1 [class.union.general] paragraph 7 is overly broad, presumably including noexcept-specifiers in declarations of non-static data members of function pointer type and default arguments in class-scope lambda expressions.

Suggested resolution:

> A complete-class context of a class (template) or class template `C` is a
> - function body (9.6.1 [dcl.fct.def.general]),
> - default argument (9.3.4.7 [dcl.fct.default]) of a function declaration,
> - default template argument (13.2 [temp.param]),
> - noexcept-specifier (14.5 [except.spec]) of a function declaration, or
> - default member initializer, or
> - complete-class context of a nested class (11.4.12 [class.nest]) defined in `C`, recursively,
> where the function, template, non-static data member, or nested class is declared by a member-declaration of `C` within the member-specification of the class or class template. [Note 4: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class. —end note]

#### 2979. Duplicate declarations of enumerations in class scope

Section: 11.4.1 [class.mem.general] Status: open Submitter: Richard Smith Date: 2024-12-31

Consider:

      struct S {
        enum B {
          A
        };
        using B::A;
        // or:
        // using enum B ;
      };

This is neither prohibited by 11.4.1 [class.mem.general] paragraph 6 (because using-declarators are not members per 11.4.1 [class.mem.general] paragraph 3) nor by 9.10 [namespace.udecl] paragraph 8.

Possible resolution:

1. Change in 9.10 [namespace.udecl] paragraph 8 as follows:

   > [ Note: If a declaration is named by two using-declarators that inhabit the same class scope, the program is ill-formed (11.4.1 [class.mem.general]). [Example: ... -- end example ] -- end note ]

2. Change in 11.4.1 [class.mem.general] paragraph 6 as follows:

   > A member An entity shall not be declared or named by a using-declarator (9.10 [namespace.udecl]) twice in the member-specification, except that
   > - a nested class or member class template can be declared and then later defined, and
   > - an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.
   > [ Example:

   >       struct S {
   >         class C;
   >         class C {};      // OK
   >         enum E { e };
   >         using E::e;      // error: redeclaration of e
   >       };

   > -- end example ]

   > [Note 4: A single name can denote several member functions provided their types are sufficiently different (6.4.1 [basic.scope.scope]). -- end note]

#### 3051. Missing specification for types of member subobjects

Section: 11.4.1 [class.mem.general] Status: open Submitter: Brian Bi Date: 2025-07-09

(From submission #726.)

The standard does not appear to specify the types of member subobjects. In particular, the manner in which the cv-qualifiers of the type of the enclosing class object combine with those of the member declaration is specified only for member access expressions (7.6.1.5 [expr.ref]), not for the type of the subobject itself.

Suggested resolution:

1. Change in 7.6.1.5 [expr.ref] bullet 8.2 and 8.6 as follows:

   > - ...
   > - Otherwise, if E2 designates a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the corresponding member subobject of the object designated by E1 and its type is the type that a member subobject corresponding to the member designated by `E2` would have in an object whose type is that of `E1` (11.4.1 [class.mem.general]). If E1 is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue. Let the notation vq12 stand for the “union” of vq1 and vq2 ; that is, if vq1 or vq2 is volatile, then vq12 is volatile. Similarly, let the notation cq12 stand for the “union” of cq1 and cq2 ; that is, if cq1 or cq2 is const, then cq12 is const. If the entity designated by E2 is declared to be a mutable member, then the type of E1.E2 is “vq12 T”. If the entity designated by E2 is not declared to be a mutable member, then the type of E1.E2 is “cq12 vq12 T”.
   > - ...
   > - Otherwise, if E2 designates a direct base class relationship (D, B) and the type of E1 is cv T, the expression designates the direct base class subobject of type B "cv `B`" of the object designated by E1. If E1 is an lvalue, then E1.E2 is an lvalue; otherwise, E1.E2 is an xvalue. The type of E1.E2 is “cv B”.

2. Change in 11.4.1 [class.mem.general] paragraph 6 as follows:

   > Every object of class type has a unique member subobject corresponding to each of its direct non-static data members. For a non-static data member of non-reference type "cq2 vq2 `T`" that is a direct member of an object whose type is "cq1 vq1 `C`", let vq12 be `volatile` if either `vq1` or `vq2` is volatile, and empty otherwise, and let cq12 be `const` if either cq1 or cq2 is `const`, and empty otherwise. If the member is declared `mutable`, then the type of the corresponding subobject is "vq12 `T`"; otherwise, the type of the corresponding subobject is "cq12 vq12 `T`". If any non-static data member of a class C is of reference type, then let D be an invented class that is identical to C except that each non-static member of D corresponding to a member of C of type “reference to T” instead has type “pointer to T”. Every member subobject of a complete object of type C has the same size, alignment, and offset as that of the corresponding subobject of a complete object of type D. The size and alignment of C are the same as the size and alignment of D.

3. Change in 11.7.2 [class.mi] paragraph 4 as follows:

   > A base class specifier that does not contain the keyword virtual specifies a non-virtual base class. A base class specifier that contains the keyword virtual specifies a virtual base class. For each distinct occurrence of a non-virtual base class `A` in the class lattice of the most derived class `C`, the a most derived object (6.8.2 [intro.object]) of type "cv `C`" shall contain a corresponding distinct base class subobject of that type "cv `A`". For each distinct base class `B` that is specified virtual, the most derived object shall contain a single base class subobject of that type "cv `B`".

#### 2787. Kind of explicit object copy/move assignment function

Section: 11.4.4 [special] Status: open Submitter: Corentin Jabot Date: 2023-08-08

Consider:

      struct S {
        auto & operator=(this S &, S&&);
        auto & operator=(this S &, const S&);
      };

The rule in 11.4.4 [special] paragraph 5 does not treat explicit-object member functions correctly:

> Two special member functions are of the same kind if:
> - they are both default constructors,
> - they are both copy or move constructors with the same first parameter type, or
> - they are both copy or move assignment operators with the same first parameter type and the same cv-qualifiers and ref-qualifier, if any.

Possible resolution:

Change in 11.4.4 [special] paragraph 5 as follows:

> Two special member functions are of the same kind if:
> - they are both default constructors,
> - they are both copy or move constructors with the same first parameter type, or
> - they are both copy or move assignment operators with the same first non-object parameter type and, the same cv-qualifiers object parameter type, and either both are implicit object member functions with no ref-qualifier, if any or neither of them is.

#### 2841. When do const objects start being const?

Section: 11.4.5.1 [class.ctor.general] Status: open Submitter: Tom Honermann Date: 2023-12-14

Subclause 11.4.5.1 [class.ctor.general] paragraph 5 specifies:

> A constructor can be invoked for a const, volatile or const volatile object. const and volatile semantics (9.2.9.2 [dcl.type.cv]) are not applied on an object under construction. They come into effect when the constructor for the most derived object (6.8.2 [intro.object]) ends.

This is referring to the "constructor ending", but 6.8.4 [basic.life] bullet 1.3 refers to "the initialization is complete". These two points in the evaluation are different at least for delegating constructors and named return value optimization, and thus the rules are in conflict.

Possible resolution:

Change in 11.4.5.1 [class.ctor.general] paragraph 5 as follows:

> [ Note: A constructor can be invoked for a const, volatile or const volatile object. const and volatile semantics (9.2.9.2 [dcl.type.cv]) are not applied on an object under construction. They ; they come into effect when the constructor for the most derived object (6.8.2 [intro.object]) ends initialization of the result object completes (6.8.4 [basic.life]). -- end note ]

CWG 2024-02-02

There are various properties that come to be for an object during its initialization. For example, the vtable is fully set up and the destructor will be invoked once the non-delegating constructor completes (issue 2756). The main property of a const object is its immutability from an optimizer perspective. With NRVO (11.9.6 [class.copy.elision]), it is possible to have multiple defining declarations for the same object, for example:

      struct A {
        int x;
      };
      int foo(const A* p)
      {
        A a{10};            // #1
        a.x = 1 + p->x;     // what's the value of a.x?
        return a;
      }
      const A g = foo(&g);  // #2

Given NRVO, both #1 and #2 are declarations of the same object. Those declarations may differ in their cv-qualification for the object. CWG tentatively agreed on the direction that the innermost declaration defines the object's properties, in particular whether it has been initialized and whether it is const.

While NRVO is disallowed in constant evaluation, it was noted that user code can determine whether NRVO occurs at runtime by comparing a pointer to the local object with a pointer to the object in the surrounding context.

#### 2999. Trivial unions changing existing behavior

Section: 11.4.5.2 [class.default.ctor] Status: open Submitter: Richard Smith Date: 2025-02-14 Liaison: EWG

Consider:

      union U { int a, b; };
      template<U u> class X {};
      constexpr U make() { U u; return u; }
      void f(X<make()>) {}

Before P3074R7, the template argument of `X` was a union object with no active member. Now, the default construction of `U` starts the lifetime of `U::a` because it has implicit-lifetime type (11.4.5.2 [class.default.ctor] paragraph 4).

This changes the mangling of `f`, because a union with no active member is different from a union with the first member active. More importantly, it is now ill-formed, because `U::a` is in-lifetime, but uninitialized (7.7 [expr.const] bullet 22.2).

Also consider the following implementation divergence

      struct E { };
      union U { E e; };
      template<U u> class X {};

      constexpr U make() { U u; return u; }
      constexpr U make2() { U u{}; return u; }

      void f(X<make()>) {}
      void f(X<make2()>) {}    // OK with clang; redefinition error with gcc

If any of those behavior changes are intended, an Annex C entry is needed.

Additional notes (September, 2025)

See also P3726R0 (Adjustments to Union Lifetime Rules).

Forwarding to EWG to clarify the intent with paper issue #2418.

#### 1548. Copy/move construction and conversion functions

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

The current wording of 11.4.5.3 [class.copy.ctor] paragraph 31 refers only to constructors and destructors:

> When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.

However, in some cases (e.g., `auto_ptr`) a conversion function is also involved in the copying, and it could presumably also have visible side effects that would be eliminated by copy elision. (Some additional contexts that may also require changes in this regard are mentioned in the resolution of issue 535.)

Additional note (September, 2012):

The default arguments of an elided constructor can also have side effects and should be mentioned, as well; however, the elision should not change the odr-use status of functions and variables appearing in those default arguments.

#### 2743. Copying non-trivial objects nested within a union

Section: 11.4.5.3 [class.copy.ctor] Status: open Submitter: Jiang An Date: 2023-06-05

Consider:

      #include<new>

      struct A {
        ~A();
      };

      union U {
        alignas(A) unsigned char buf[sizeof(A)];
      };

      void f() {
        U u;
        ::new (u.buf) A();  // object of type A is nested within u because u provides storage
        U v = u;            // #1
      }

Subclause 11.4.5.3 [class.copy.ctor] paragraph 15 specifies:

> The implicitly-defined copy/move constructor for a union X copies the object representation (6.9.1 [basic.types.general]) of X. For each object nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.

Thus, the rule seems to require that #1 creates an object of type `A` inside `v.buf` (without invoking a constructor of `A`), and then a copy of the object representation is performed. Creating an object out of thin air can plausibly work only for implicit-lifetime types (11.2 [class.prop] paragraph 9), and meaningfully copying the object representation works only for trivially copyable types (6.9.1 [basic.types.general] paragraph 3). The rule about nested objects should be suitably limited.

Subclause 11.4.6 [class.copy.assign] paragraph 13 has a similar rule for copy/move assignment:

> The implicitly-defined copy/move assignment operator for a union X copies the object representation (6.9.1 [basic.types.general]) of X. If the source and destination of the assignment are not the same object, then for each object nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.

Possible resolution:

1. Change in 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

   > The implicitly-defined copy/move constructor for a union X copies the object representation (6.9.1 [basic.types.general]) of X. For each object of implicit-lifetime type (6.9.1 [basic.types.general]) nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.

2. Change in 11.4.6 [class.copy.assign] paragraph 13 as follows:

   > The implicitly-defined copy/move assignment operator for a union X copies the object representation (6.9.1 [basic.types.general]) of X. If the source and destination of the assignment are not the same object, then for each object of implicit-lifetime type (6.9.1 [basic.types.general]) nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.

#### 2837. Instantiating and inheriting by-value copy constructors

Section: 11.4.5.3 [class.copy.ctor] Status: open Submitter: Brian Bi Date: 2023-11-29

(From submission #474.)

Subclause 11.4.5.3 [class.copy.ctor] paragraph 5 specifies:

> A declaration of a constructor for a class X is ill-formed if its first parameter is of type cv X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to produce such a constructor signature.

It is unclear what happens when such a constructor signature is inherited from a base class. Also, the wording "never instantiated" is not detailed enough.

Suggested resolution:

1. Add a paragraph before 9.10 [namespace.udecl] paragraph 13 as follows:

   > The set of declarations named by a using-declarator that inhabits a class `C` does not include any constructor whose first parameter has type cv `C` and all of whose remaining parameters (if any) have default arguments.

   > Constructors that are named by a using-declaration are treated as though they were constructors of the derived class ...

2. Change in 11.4.5.3 [class.copy.ctor] paragraph 5 as follows:

   > A declaration of a constructor for a class X is ill-formed if its first parameter is of type cv X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to produce such a constructor signature. During type deduction for a constructor template of `X` (13.10.3.1 [temp.deduct.general]), if substitution produces such a constructor signature, type deduction fails. [ Example 5:

   >       struct S {
   >         template<typename T> S(T);
   >         S();
   >       };
   >       S g;
   >       void h() {
   >         S a(g);  // does not instantiate the member template to produce S::S<S>(S); no candidate generated from the member template;
   >                  // uses the implicitly declared copy constructor is used
   >       }

   > -- end example]

#### 2329. Virtual base classes and generated assignment operators

Section: 11.4.6 [class.copy.assign] Status: open Submitter: Daveed Vandevoorde Date: 2016-10-31

An example like the following,

      class A {
      private:
        A& operator=(const A&);
      };

      class B : virtual public A {
      public:
        B& operator = (const B& src);
      };

      class C: public B {
      public:
        void f(const C* psrc) {
          *this = *psrc;
        }
      };

is presumably well-formed, even though the copy assignment operator of `A` is inaccessible in `C`, because 11.4.6 [class.copy.assign] 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.

#### 2897. Copying potentially-overlapping union subobjects

Section: 11.4.6 [class.copy.assign] Status: open Submitter: Jiang An Date: 2024-05-30

(From submission #542.)

Consider:

      union U {};
      struct S {
        U u [[no_unique_address]];
        char c;
      };
      S s;
      s.c = 1;
      s.u = U();   // #1

As specified, the move-assignment at #1 might disturb the value of `s.c`.

Suggested resolution:

(Note that the value representation of a union arguably depends on which of its members is active.)

1. Change in 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

   > The implicitly-defined copy/move constructor for a union X copies the object value representation (6.9.1 [basic.types.general]) of X. For each object nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.

2. Change in 11.4.6 [class.copy.assign] paragraph 13 as follows:

   > The implicitly-defined copy/move assignment operator for a union X copies the object value representation (6.9.1 [basic.types.general]) of X. If the source and destination of the assignment are not the same object, then for each object nested within (6.8.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.

#### 1977. Contradictory results of failed destructor lookup

Section: 11.4.7 [class.dtor] Status: open Submitter: Gabriel Dos Reis Date: 2014-07-21

According to 11.4.7 [class.dtor] 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.6 [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 [class.free] paragraph 7.

#### 2839. Explicit destruction of base classes

Section: 11.4.7 [class.dtor] Status: open Submitter: Jiang An Date: 2023-12-07

(From submission #478.)

Consider:

      struct B {};
      struct D1 : virtual B {};
      struct D2 : virtual B {};
      struct D : D1, D2 {};

      void f() {
        D *d = new D;
        static_cast<D2&>(*d).~D2();
      }

According to 11.4.7 [class.dtor] paragraph 13, invoking a destructor for a most derived object is different from invoking it for a base class subobject:

> After executing the body of the destructor and destroying any objects with automatic storage duration allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X's non-virtual direct base classes and, if X is the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes.

However, there is no means to convey the difference in an explicit destructor call. As an aside, potentially-overlapping subobjects cannot be transparently replaced, thus any attempt at replacement would implicitly end the lifetime of the complete object due to storage reuse.

Possible resolution:

Split 7.6.1.3 [expr.call] paragraph 4 and amend, as follows:

> ..., even if the type of the function actually called is different.

> If the postfix-expression P names a destructor or a pseudo-destructor, the postfix-expression is a possibly-parenthesized class member access. If P names a destructor, the behavior is undefined if the object expression denotes a base class subobject and that base class is or has a virtual base class or a virtual function (11.4.7 [class.dtor]). If the postfix-expression P names a pseudo-destructor (in which case the postfix-expression is a possibly-parenthesized class member access), the function call destroys the object of scalar type denoted by the object expression of the class member access (7.6.1.5 [expr.ref], 6.8.4 [basic.life]).

#### 3041. Overly aggressive rule for deleting the destructor of a union

Section: 11.4.7 [class.dtor] Status: open Submitter: Barry Revzin Date: 2025-07-03

(From submission #722.)

Paper P3074R7 added an overly aggressive rule to delete destructors of unions, making the following example have a deleted destructor:

      union U {
        U(int i) : i(i) { }
        int i;
      };

Suggested resolution:

Change in 11.4.7 [class.dtor] paragraph 7 as follows:

> A defaulted destructor for a class X is defined as deleted if
> - X is a non-union class and any non-variant potentially constructed subobject has class type M (or possibly multidimensional array thereof) where M has a destructor that is deleted or is inaccessible from the defaulted destructor,
> - X is a union and there is a subobject S of class type `M` (or possibly multi-dimensional array thereof) where `M` has a destructor that is deleted, inaccessible from the defaulted destructor, or non-trivial, and either
>   - overload resolution to select a constructor to default-initialize an object of type X either fails or selects a constructor that is either deleted or not trivial user-provided, or
>   - X has a variant member V of class type M (or possibly multi-dimensional array thereof) where V S has a default member initializer and M has a destructor that is non-trivial, or,
> - for a virtual destructor, lookup of the non-array deallocation function results in an ambiguity or in a function that is deleted or inaccessible from the defaulted destructor.

#### 2513. Ambiguity with requires-clause and operator-function-id

Section: 11.4.8.3 [class.conv.fct] Status: open Submitter: Richard Smith Date: 2021-12-09

An ambiguity can occur with a requires-clause that ends in an operator-function-id:

      template<typename T> requires T::operator int
      const // part of operator-type-id or return type?
      unsigned f();

      template<typename T> requires T::operator int
      [[attr]] // appertains to type int or to declaration of g?
      void g();

Such cases are always ill-formed, because they involve an atomic constraint of non-`bool` type.

#### 1283. Static data members of classes with typedef name for linkage purposes

Section: 11.4.9.3 [class.static.data] Status: open Submitter: Mike Miller Date: 2011-03-29

According to 11.4.9.3 [class.static.data] paragraph 4,

> Unnamed classes and classes contained directly or indirectly within unnamed classes shall not contain static data members.

There is no such restriction on member functions, and there is no rationale for this difference, given that both static data members and member functions can be defined outside a unnamed class with a typedef name for linkage purposes. (Issue 406 acknowledged the lack of rationale by removing the specious note in 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.

#### 2950. Value preservation in enumeration vs. integer bit-fields

Section: 11.4.10 [class.bit] Status: open Submitter: Geoffrey Romer Date: 2024-05-13

Subclause 11.4.10 [class.bit] paragraph 4 specifies:

> If a value of integral type (other than bool) is stored into a bit-field of width N and the value would be representable in a hypothetical signed or unsigned integer type with width N and the same signedness as the bit-field's type, the original value and the value of the bit-field compare equal. If the value true or false is stored into a bit-field of type bool of any size (including a one bit bit-field), the original bool value and the value of the bit-field compare equal. If a value of an enumeration type is stored into a bit-field of the same type and the width is large enough to hold all the values of that enumeration type (9.8.1 [dcl.enum]), the original value and the value of the bit-field compare equal.

Thus, for bit-fields of integral type, it is sufficient for the actual value to fit into the bit-field. In contrast, for bit-fields of enumeration type, all values of the enumeration type are required to fit even if the actual value being stored is small and would fit. For example:

      enum E : uint8_t { E0 = 0, E1 = 1 };
      struct S {
        int i : 1;
        E e : 1;
      };

      void f() {
        S s = { .i = 1, .e = 1 };
        assert(s.i == 1);    // guaranteed to hold
        assert(s.e == 1);    // may not hold
      }

Suggested resolution:

Change in 11.4.10 [class.bit] paragraph 4 as follows:

> If a value of integral type (other than bool) or of enumeration type is stored into a bit-field of width N and the value would be representable in a hypothetical signed or unsigned integer type with width N and the same signedness as the bit-field's type or, respectively, its underlying type, the original value and the value of the bit-field compare equal. If the value true or false is stored into a bit-field of type bool of any size (including a one bit bit-field), the original bool value and the value of the bit-field compare equal. If a value of an enumeration type is stored into a bit-field of the same type and the width is large enough to hold all the values of that enumeration type (9.8.1 [dcl.enum]), the original value and the value of the bit-field compare equal.

#### 57. Empty unions

Section: 11.5 [class.union] Status: open Submitter: Steve Adamczyk Date: 13 Oct 1998

There doesn't seem to be a prohibition in 11.5 [class.union] against a declaration like

        union { int : 0; } x;

Should that be valid? If so, 9.5 [dcl.init] paragraph 5 third bullet, which deals with default-initialization of unions, should say that no initialization is done if there are no data members.

What about:

        union { } x;
        static union { };

If the first example is well-formed, should either or both of these cases be well-formed as well?

(See also the resolution for issue 151.)

Notes from 10/00 meeting: The resolution to issue 178, which was accepted as a DR, addresses the first point above (default initialization). The other questions have not yet been decided, however.

#### 1404. Object reallocation in unions

Section: 11.5 [class.union] Status: open Submitter: Nikolay Ivchenkov Date: 2011-10-19

According to 11.5 [class.union] paragraph 4,

> [Note: In general, one must use explicit destructor calls and placement new operators to change the active member of a union. —end note] [Example: Consider an object `u` of a union type `U` having non-static data members `m` of type `M` and `n` of type `N`. If `M` has a non-trivial destructor and `N` has a non-trivial constructor (for instance, if they declare or inherit virtual functions), the active member of `u` can be safely switched from `m` to `n` using the destructor and placement new operator as follows:

>       u.m.~M();
>       new (&u.n)  N;

> —end example]

This pattern is only “safe” if the original object that is being destroyed does not involve any const-qualified or reference types, i.e., satisfies the requirements of 6.8.4 [basic.life] 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.8.4 [basic.life].

Additional note (October, 2013):

See also issue 1776, which suggests possibly changing the restriction in 6.8.4 [basic.life]. If such a change is made, this issue may become moot.

#### 2675. `start_lifetime_as`, placement-new, and active union members

Section: 11.5.1 [class.union.general] Status: open Submitter: Nina Ranns Date: 2022-12-06

Consider:

      struct A { int i;};
      struct B { int j;};
      union U { A a; B b; };
      U u1{A{}};
      start_lifetime_as<B>(&u1.a); // #1
      start_lifetime_as<B>(&u1);   // #2
      U u2{B{}};
      start_lifetime_as<U>(&u2);   // #3
      start_lifetime_as<B>(&u1.b); // #4

It is unclear which of these constructs changes the active member of the union, and whether a union might have more than one active member. Subclause 11.5.1 [class.union.general] paragraph 2 appears contradictory if several members can be in-lifetime:

> In a union, a non-static data member is active if its name refers to an object whose lifetime has begun and has not ended (6.8.4 [basic.life]). At most one of the non-static data members of an object of union type can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

Similar questions arise for placement-new and for `construct_at`; for the latter also during constant evaluation.

#### 2767. Non-defining declarations of anonymous unions

Section: 11.5.2 [class.union.anon] Status: open Submitter: Hubert Tong Date: 2023-07-06

Currently, the specification permits this example:

      namespace {
        extern union { int x, y; };
        union { int x, y; };
      };

However, those non-defining declarations seem to be mostly useless, because any definition attempt would violate the one-definition rule (6.3 [basic.def.odr]).

Possible resolution (updated 2023-10-21):

This resolution also addresses issue 1702.

1. Split 11.5.2 [class.union.anon] paragraph 1 and change as follows:

   > A union of the form A simple-declaration or member-declaration of the form

   >        attribute-specifier-seqopt decl-specifier-seq ;

   > where one of the decl-specifiers is a class-specifier of the form

   >       union attribute-specifier-seqopt { member-specification } ;

   > is called declares an anonymous union; it defines an unnamed type `T` and an unnamed object of that type cv `T` called an anonymous union member if it is a non-static data member or an anonymous union variable otherwise, where cv is determined from the cv-qualifiers of the declaration. The optional attribute-specifier-seq in the simple-declaration or member-declaration appertains to the data member or variable, respectively. The storage-class-specifier `extern` (9.2.2 [dcl.stc]) shall not be among the decl-specifiers declaring an anonymous union. The declaration of an anonymous union shall not be the name-declaration of a linkage-specification (9.12 [dcl.link]).

   > Each member-declaration in the member-specification of an anonymous union shall either define one or more public non-static data members or be a static_assert-declaration. Nested types, anonymous unions, and functions shall not be declared within an anonymous union. The names of the members of an anonymous union are bound in the scope inhabited by the union declaration.

2. Change in 11.5.2 [class.union.anon] paragraph 2 as follows:

   > Anonymous unions declared in the scope of An anonymous union whose declaration inhabits a namespace scope with external linkage shall be declared `static`. Anonymous unions declared at block scope shall be declared with any storage class allowed for a block variable, or with no storage class. A storage class is not allowed in a declaration of an anonymous union in a class scope. An anonymous union whose declaration inhabits a class scope shall not be declared with a storage-class-specifier.

#### 2773. Naming anonymous union members as class members

Section: 11.5.2 [class.union.anon] Status: open Submitter: Hubert Tong Date: 2023-07-16

Consider:

      template<typename T>
      int f(T *tp) { return tp->x; }

      static union { int x = f(this); };

According to 11.5.2 [class.union.anon] paragraph 1:

> ... The names of the members of an anonymous union are bound in the scope inhabited by the union declaration.

Thus, the example above is ill-formed, because the member names are not bound in the scope of the class of the anonymous union. However, there is implementation divergence: clang and gcc accept the example (contrary to the wording), icc rejects. Notwithstanding, `this` refers to the anonymous union itself, not to an enclosing class, per 7.5.3 [expr.prim.this]. This rule causes rejection of

      struct A {
        int foo();
        union { int x = foo(); };  // error
      };
      A a;

Alternatively, `this` could be made to refer to the enclosing class object (already the status quo for some implementations). However, that would cause inconsistent treatment for examples like the following:

      struct A {
        int n;
        union {
          void *p = this; // A*
        };
      };

vs.

      struct B {
        int n;
        union {
          void *p = this; // decltype(u)*
        } u;
      };

Possible resolution:

tbd

#### 3035. Lambda expressions in anonymous unions

Section: 11.5.2 [class.union.anon] Status: open Submitter: Hubert Tong Date: 2025-05-12

(From submission #706.)

Consider:

      static union {
        int x = [] { return 42; }();
      };

According to 11.5.2 [class.union.anon] paragraph 1, this is ill-formed, because the closure type is declared as a nested type of the anonymous union (7.5.6.2 [expr.prim.lambda.closure] paragraph 3, but some implementations do not yield a diagnostic.

On the other hand, the code shown in the example is reasonable and ought to be allowed.

Suggested resolution (emphasizing the prohibition):

Change in 11.5.2 [class.union.anon] paragraph 1 as follows:

> ... Nested types, including closure types (7.5.6.2 [expr.prim.lambda.closure]) and anonymous unions, and functions shall not be declared within an anonymous union....

#### 1699. Does befriending a class befriend its friends?

Section: 11.8.4 [class.friend] Status: open Submitter: David Krauss Date: 2013-06-12 Liaison: EWG

According to 11.8.4 [class.friend] paragraph 2,

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

A `friend` declaration is a member-declaration, but it is not clear how far the granting of friendship goes in a `friend` declaration. For example:

      class c {
        class n {};
        friend struct s;
      };

      struct s {
        friend class c::n;          // #1
        friend c::n g();            // #2
        friend void f() { c::n(); } // #3
      };

In particular, if a friend function is defined inside the class definition, as in #3, does its definition have access to the private and protected members of the befriending class? Implementations vary on this point.

Additional note (June, 2021):

The initial opinion of CWG (at the September, 2013 meeting) was that “member declarations” was intended to be the English equivalent of the syntactic nonterminal member-declaration, including a `friend` declaration/definition inside the member-specification of a class, making #3 well-formed. However, recent discussion has expressed concern over the different treatment of in-class and out-of-class definitions of friend functions and observed that there is still divergence among implementations.

Rationale (November, 2021):

There are two lines of analysis that lead to opposite conclusions. The first is that a friend defined within the member-specification is written by the class author and is effectively part of the class, not subject to hijacking by other declarations, and thus should be afforded the same access as all other declarations that are part of the class. The second is that giving different access to a friend function based simply on whether it was defined inside or outside of its befriending class is confusing.

CWG considered this to be a design-level question, not simply to be determined by the usual relationship between English and grammar terms, and thus is asking EWG for its opinion.

Additional note (May, 2023)

Forwarded to EWG via cplusplus/papers#1573.

#### 472. Casting across protected inheritance

Section: 11.8.5 [class.protected] Status: open 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 {
            int i;
        };
        struct I: protected B { };
        struct D: I {
            void f(I* ip) {
                B* bp = ip;    // well-formed?
                bp->i = 5;     // aka "ip->i = 5;"
            }
        };

I think the rationale for the 11.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 [class.access.base] paragraph 4 clearly makes the conversion from `I*` to `B*` well-formed. I think that's wrong and needs to be fixed. The rationale for the accessibility of a base class is whether “an invented public member” of the base would be accessible at the point of reference, although we obscured that a bit in the reformulation; it seems to me that the invented member ought to be considered a non-static member for this purpose and thus subject to 11.8.5 [class.protected].

(See also issues 385 and 471.).

Notes from October 2004 meeting:

The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.8.5 [class.protected].

Proposed resolution (April, 2011)

Change 11.8.3 [class.access.base] 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:

>         class B {
>         public:
>           int m;
>         };

>         class S: private B {
>           friend class N;
>         };
>         class N: private S {
>           void f() {
>             B* p = this;    // OK because class S satisfies the fourth condition
>                             // above: B is a base class of N accessible in f() because
>                             // B is an accessible base class of S and S is an accessible
>                             // base class of N.
>           }
>         };

>         class N2: protected B { };

>         class P2: public N2 {
>           void f2(N2* n2p) {
>             B* bp = n2p;    // error: invented member would be protected and naming
>                             // class N2 not the same as or derived from the referencing
>                             // class P2
>             n2p->m = 0;     // error (cf 11.8.5 [class.protected]) for the same reason
>           }
>         };

> —end example]

#### 2244. Base class access in aggregate initialization

Section: 11.8.5 [class.protected] Status: open Submitter: Richard Smith Date: 2016-03-08

The rules in 11.8.5 [class.protected] assume an object expression, perhaps implicit, that can be used to determine whether access to protected members is permitted or not. It is not clear how that applies to aggregates and constructors. For example:

      struct A {
      protected:
        A();
      };
      struct B : A {
        friend B f();
        friend B g();
        friend B h();
      };
      B f() { return {}; }     // ok?
      B g() { return {{}}; }   // ok?
      B h() { return {A{}}; }  // ok?

Notes from the December, 2016 teleconference:

The consensus favored accepting `f` and `g` while rejecting `h`.

Notes from the March, 2018 meeting:

CWG affirmed the earlier direction and felt that there should be an implicit object expression assumed for these cases.

#### 1915. Potentially-invoked destructors in non-throwing constructors

Section: 11.9.3 [class.base.init] Status: open Submitter: Aaron Ballman Date: 2014-04-15 Liaison: EWG

The requirement in 11.9.3 [class.base.init] paragraph 10,

> In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked (11.4.7 [class.dtor]). [Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (14.3 [except.ctor]). —end note]

is needlessly restrictive, preventing otherwise-reasonable code like

      class Base {
      protected:
        Base(int i) noexcept {}
        Base() = delete;
        ~Base() = delete;
      };

      class Derived : public Base {
      public:
        Derived() noexcept : Base(1) {}
        ~Derived() = delete;
      };

Since the derived class constructor is non-throwing, the deleted base class destructor need not be referenced.

Suggested resolution:

Change 11.9.3 [class.base.init] paragraph 10 as follows:

> In a non-delegating constructor without a non-throwing exception-specification (14.5 [except.spec]), the destructor for each potentially constructed subobject of class type is potentially invoked (11.4.7 [class.dtor]). [Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (14.3 [except.ctor]) but does not prevent explicitly deleted destructors in the presence of a non-throwing constructor. —end note]

Rationale (June, 2014):

This request for a language change should be evaluated by EWG before any action is taken.

EWG 2022-11-11

This is a defect, but has ABI impact that should be explored in a paper to EWG. This is tracked in github issue cplusplus/papers#1371.

#### 2056. Member function calls in partially-initialized class objects

Section: 11.9.3 [class.base.init] Status: open Submitter: Richard Smith Date: 2014-12-11

According to 11.9.3 [class.base.init] 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,

      class B {
      public:
        int f();
      };

      class C {
      public:
        C(int);
      };

      class D : public B, C {
      public:
        D() : C(f())  // undefined: calls member function
                      // but base \tcode{C} not yet initialized
        {}
      };

However, the construction of `B`, the object for which the member function is being called) has completed its construction, so it is not clear why this should be undefined behavior.

(See also issue 1517.)

#### 2669. Lifetime extension for aggregate initialization

Section: 11.9.3 [class.base.init] Status: open Submitter: Ed Catmur Date: 2022-12-18 Liaison: EWG

Issue 1815 presented a subset of the following examples:

      struct A {};
      struct B { A&& a = A{}; };
      B b1;          // #1, default-initialization
      B b2{A{}};     // #2, aggregate initialization
      B b3{};        // #3, aggregate initialization
      B b4 = B();    // #4, value-initialization
      B b5(A{});     // #5, aggregate initialized via parentheses (9.5.1 [dcl.init.general] bullet 16.6.2.2)
      struct C { int i; A&& a = A{}; };
      C c6(1);       // #6, aggregate initialized via parentheses
      A a7 = C(1).a; // #7, aggregate initialized via parentheses

Issue 1696 was adopted, ostensibly resolving issue 1815, but the wording changes in 11.9.3 [class.base.init] paragraph 11 affected only the behavior of constructors, not of aggregate initialization:

> A temporary expression bound to a reference member from a default member initializer is ill-formed. [ Example:

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

> -- end example]

There is considerable implementation variance: #1 is rejected by clang, but accepted by gcc and EDG (with early destruction of the temporary); #2 is uniformly accepted and the lifetime of the temporary is extended; #3 is uniformly accepted, but only gcc, clang, and MSVC extend the lifetime of the temporary, whereas EDG does not.

Note that 9.5.1 [dcl.init.general] paragraph 7 specifies that default-initialization of aggregates is as-if the initializer `()` is used:

> To default-initialize an object of type T means:
> - If T is a (possibly cv-qualified) class type (Clause 11 [class]), constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one for the initializer () is chosen through overload resolution (12.2 [over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.
> - If T is an array type, each element is default-initialized.
> - Otherwise, no initialization is performed.

Such treatment causes early destruction of temporaries per 6.8.7 [class.temporary] bullet 6.10:

> The exceptions to this lifetime rule are:
> - ...
> - A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list (9.5 [dcl.init]) persists until the completion of the full-expression containing the expression-list.

CWG 2023-05-12

CWG is soliciting the design guidance of EWG to resolve this issue, in particular for examples #3 and #6. For each of the presented examples, the standard should say whether the use is well-formed or not and whether the lifetime of the temporary is extended. It is also conceivable to implicitly delete the constructor being invoked (if any).

- #1 is default-initialization, invoking the default constructor of `B`. This is ill-formed per 11.9.3 [class.base.init] paragraph 11. The rationale is that the temporary would not be lifetime-extended, causing a dangling reference as soon as the constructor returns. The presence of a hypothetically lifetime-extended temporary cannot be persisted from the constructor invocation to the eventual destruction of the object.
- #2 is aggregate initialization; the temporary in the braced-init-list is lifetime-extended per 6.8.7 [class.temporary] paragraph 6. Since the temporary is created in the initializer and not inside a constructor invocation, tracking the lifetime of the temporary is equivalent to tracking the lifetime of the variable `b2`, so extending the lifetime of the temporary is feasible and avoids a dangling reference.
- #3 might be perceived as value-initialization, but is in fact also aggregate initialization; the status quo wording lifetime-extend the temporary per 6.8.7 [class.temporary] paragraph 6. However, that is inconsistent with #4.
- #4 is value-initialization; the default constructor is invoked per 9.5.1 [dcl.init.general] bullet 16.6.2.1, which is ill-formed similar to #1.
- #5 is aggregate-by-parentheses initialization. Consistent with other parenthesized initialization, the lifetime of temporaries is not extended (6.8.7 [class.temporary] bullet 6.10).
- #6 is also aggregate-by-parentheses initialization; a constructor is not invoked and the lifetime of the temporary is not extended per 6.8.7 [class.temporary] bullet 6.10, causing a dangling reference. However, despite the hidden temporary, this use is not ill-formed, because 11.9.3 [class.base.init] does not apply to aggregate initialization.
- Finally, #7 shows a situation where, unlike #6, the short lifetime of the hidden temporary is not an issue.

CWG recommends to leave #3 as-is and to make #6 (and thus #7) ill-formed. The trigger is the use of a default member initializer for a reference member that causes binding a temporary to that reference.

Forwarded to EWG via cplusplus/papers#1511.

#### 1517. Unclear/missing description of behavior during construction/destruction

Section: 11.9.5 [class.cdtor] Status: open Submitter: Daniel Krügler Date: 2012-07-07

The current wording of 11.9.5 [class.cdtor] paragraph 4 does not describe the behavior of calling a virtual function in a mem-initializer for a base class, only for a non-static data member. Also, the changes for issue 1202 should have been, but were not, applied to the description of the behavior of `typeid` and `dynamic_cast` in paragraphs 5 and 6.

In addition, the resolution of issue 597 allowing the out-of-lifetime conversion of pointers/lvalues to non-virtual base classes, should have been, but were not, applied to paragraph 3.

(See also issue 2056.)

Proposed resolution (August, 2013):

1. Change 11.9.5 [class.cdtor] 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 X { int i; };
>       struct Y : X { Y(); };                       // non-trivial
>       struct A { int a; };
>       struct B : public virtual A { int j; Y y; }; // non-trivial

>       extern B bobj;
>       B* pb = &bobj;                               // OK
>       int* p1 = &bobj.a;                           // undefined, refers to base class member
>       int* p2 = &bobj.y.i;                         // undefined, refers to member's member

>       A* pa = &bobj;                               // undefined, upcast to a virtual base class type
>       B bobj;                                      // definition of bobj

>       extern X xobj;
>       int* p3 = &xobj.i;                           //OK, X is a trivial class
>       X xobj;

2. 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 V {
>         V();
>         V(int);
>         virtual void f();
>         virtual void g();
>       };

>       struct A : virtual V {
>         virtual void f();
>         virtual int h();
>         A() : V(h()) { }     // undefined behavior: virtual function h called
>                              // before A's period of construction begins
>       };

>       struct B : virtual V {
>         virtual void g();
>         B(V*, A*);
>       };

>       struct D : A, B {
>         virtual void f();
>         virtual void g();
>         D() : B((A*)this, this) { }
>       };

>       B::B(V* v, A* a) {
>         f();                 // calls V::f, not A::f
>         g();                 // calls B::g, not D::g
>         v->g();              // v is base of B, the call is well-defined, calls B::g
>         a->f();              // undefined behavior, a's type not a base of B
>         typeid(*this);       // type_info for B
>         typeid(*v);          // well-defined: *v has type V, a base of B,
>                              // so its period of construction is complete;
>                              // yields type_info for B
>         typeid(*a);          // undefined behavior: A is not a base of B,
>                              // so its period of construction has not begun
>         dynamic_cast<B*>(v); // well-defined: v has type V*, V is a base of B,
>                              // so its period of construction is complete;
>                              // results in this
>         dynamic_cast<B*>(a); // undefined behavior: A is not a base of B,
>                              // so its period of construction has not begun
>       }

> —end example]

> The `typeid` operator (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_cast`s (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 V {
>         virtual void f();
>       };

>       struct A : virtual V { };

>       struct B : virtual V {
>         B(V*, A*);
>       };

>       struct D : A, B {
>         D() : B((A*)this, this) { }
>       };

>       B::B(V* v, A* a) {
>         typeid(*this);       // type_info for B
>         typeid(*v);          // well-defined: *v has type V, a base of B
>                              // yields type_info for B
>         typeid(*a);          // undefined behavior: type A not a base of B
>         dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B
>                              // results in B*
>         dynamic_cast<B*>(a); // undefined behavior,
>                              // a has type A*, A not a base of B

> —end example]

#### 1049. Copy elision through reference parameters of inline functions

Section: 11.9.6 [class.copy.elision] Status: open Submitter: Jason Merrill Date: 2010-03-10

Consider the following example:

        int c;

        struct A {
           A() { ++c; }
           A(const A&) { ++c; }
        };

        struct B {
           A a;
           B(const A& a): a(a) { }
        };

        int main() {
           (B(A()));
           return c - 1;
        }

Here we would like to be able to avoid the copy and just construct the `A()` directly into the `A` subobject of `B`. But we can't, because it isn't allowed by 11.4.5.3 [class.copy.ctor] bullet 34.3:

- when a temporary class object that has not been bound to a reference (6.8.7 [class.temporary]) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

The part about not being bound to a reference was added for an unrelated reason by issue 185. If that resolution were recast to require that the temporary object is not accessed after the copy, rather than banning the reference binding, this optimization could be applied.

The similar example using pass by value is also not one of the allowed cases, which could be considered part of issue 6.

CWG 2023-05-12

This is a plausible optimization opportunity whose detailed specification requires a paper.

#### 3007. Access checking during synthesis of defaulted comparison operator, take 2

Section: 11.10.1 [class.compare.default] Status: open Submitter: Jason Merrill Date: 2025-03-13 Liaison: EWG

Issue 2568 sought to make the following situation well-formed:

       struct Base {
       protected:
         bool operator==(const Base& other) const = default;
       };

       struct Child : Base {
         int i;
         bool operator==(const Child& other) const = default;
       };

       bool b = Child() == Child(); // error: deleted operator==

However, the applied resolution of that issue failed to achieve the intent, because the object expression has type `Base` when invoking `Base::operator==` in the synthesized body of `Child::operator==`, per 11.10.1 [class.compare.default] paragraph 5 together with 11.10.2 [class.eq] paragraph 2 and 11.10.3 [class.spaceship] paragraph 2. The synthesized body of `Child::operator==` looks like this:

       bool Child::operator==(const Child& other) const
       {
         return (static_cast<const Base&>(*this) == static_cast<const Base&>(other)); // error, protected
       }

Additional notes (March, 2025)

EWG should decide whether the design intent is to allow access to `Base::operator==` in the example. Forwarded to EWG with paper issue #2239 by decision of the CWG chair.

EWG 2025-06-16

EWG decided that the access check should succeed for the example.

#### 3031. Finding declarations for conversion operators for access checking

Section: 12.2.2.1 [over.match.funcs.general] Status: open Submitter: Brian Bi Date: 2025-03-29

Consider:

      class B {
        protected:
          operator int&() { static int x; return x; }
      };

      struct D : B { using B::operator int&; };

      int x = D();   // OK

The function `B::operator int&` is a candidate, but the using-declaration in `D` is not considered when checking accessibility, because name lookup is not actually performed.

Suggested resolution:

Change in 12.2.2.1 [over.match.funcs.general] paragraph 7 as follows:

> In each case where conversion functions of a class S are considered for initializing an object or reference of type T, the candidate functions include the result of a search for the conversion-function-id operator T in S. [Note 3: This search can find a specialization of a conversion function template (6.5 [basic.lookup]). —end note] Each such case also defines sets of permissible types for explicit and non-explicit conversion functions; for each (non-template) conversion function `F` that
> - is a non-hidden member of S,
> - yields a permissible type `U`, and,
> - for the former set, is non-explicit,
> is also a candidate function if looking up `operator U` in the scope of `S` succeeds and finds a declaration of `F`, that declaration is added to the candidate set. If initializing an object, for any permissible type cv U, any cv2 U, cv2 U&, or cv2 U&& is also a permissible type. If the set of permissible types for explicit conversion functions is empty, any candidates that are explicit are discarded.

#### 2189. Surrogate call template

Section: 12.2.2.2.3 [over.call.object] Status: open Submitter: Jason Merrill Date: 2015-10-22

Consider:

      template <class T>
      using Fn = void (*)(T);

      struct A
      {
        template <class T>
        operator Fn<T>();
      };

      int main()
      {
        A()(42);
      }

12.2.2.2.3 [over.call.object] describes how conversion functions to pointer/reference to function work in overload resolution, but is silent about conversion function templates. Generalizing the wording there, in this case we could generate a surrogate conversion template

    template <class T>
      void /surrogate/ (Fn<T> f, T a) { return f(a); }

which would work as expected. But it seems that implementations don't actually do this currently.

#### 3055. Misleading body for surrogate call function

Section: 12.2.2.2.3 [over.call.object] Status: open Submitter: Brian Bi Date: 2025-08-11

(From submission #747.)

Subclause 12.2.2.2.3 [over.call.object] paragraph 2 defines the surrogate call function has having a function body. However, this body will never be evaluated, because 12.4.4 [over.call] paragraph 1 specifies the behavior when a surrogate call function is selected by overload resolution.

Suggested resolution:

Change in 12.2.2.2.3 [over.call.object] paragraph 2 as follows:

> ..., a surrogate call function with the unique name call-function and having the form

>         R call-function ( conversion-type-id F, P1 a1, ... , Pn an) { return F (a1, ..., an); } ;

> is also considered as a candidate function. [ Note: If a surrogate call function is selected by overload resolution, the behavior is as described in 12.4.4 [over.call]. -- end note ] Similarly,...

#### 545. User-defined conversions and built-in operator overload resolution

Section: 12.2.2.3 [over.match.oper] Status: open Submitter: Steve Clamage Date: 31 October 2005

Consider the following example:

        class B1 {};
        typedef void (B1::*PB1) (); // memptr to B1

        class B2 {};
        typedef void (B2::*PB2) (); // memptr to B2

        class D1 : public B1, public B2 {};
        typedef void (D1::*PD) (); // memptr to D1

        struct S {
             operator PB1(); // can be converted to PD
        } s;
        struct T {
             operator PB2(); // can be converted to PD
        } t;

        void foo() {
             s == t; // Is this an error?
        }

According to 12.5 [over.built] paragraph 16, there is an `operator==` for `PD` (“For every pointer to member type...”), so why wouldn't it be used for this comparison?

Mike Miller: The problem, as I understand it, is that 12.2.2.3 [over.match.oper] paragraph 3, bullet 3, sub-bullet 3 is broader than it was intended to be. It says that candidate built-in operators must “accept operand types to which the given operand or operands can be converted according to 12.2.4.2 [over.best.ics].” 12.2.4.2.3 [over.ics.user] describes user-defined conversions as having a second standard conversion sequence, and there is nothing to restrict that second standard conversion sequence.

My initial thought on addressing this would be to say that user-defined conversion sequences whose second standard conversion sequence contains a pointer conversion or a pointer-to-member conversion are not considered when selecting built-in candidate operator functions. They would still be applicable after the hand-off to Clause 5 (e.g., in bringing the operands to their common type, 7.6.10 [expr.eq], or composite pointer type, 7.6.9 [expr.rel]), just not in constructing the list of built-in candidate operator functions.

I started to suggest restricting the second standard conversion sequence to conversions having Promotion or Exact Match rank, but that would exclude the Boolean conversions, which are needed for `!`, `&&`, and `||`. (It would have also restricted the floating-integral conversions, though, which might be a good idea. They can't be used implicitly, I think, because there would be an ambiguity among all the promoted integral types; however, none of the compilers I tested even tried those conversions because the errors I got were not ambiguities but things like “floating point operands not allowed for `%`”.)

Bill Gibbons: I recall seeing this problem before, though possibly not in committee discussions. As written this rule makes the set of candidate functions dependent on what classes have been defined, including classes not otherwise required to have been defined in order for "==" to be meaningful. For templates this implies that the set is dependent on what templates have been instantiated, e.g.

      template<class T> class U : public T { };
      U<B1> u;  // changes the set of candidate functions to include
                // operator==(U<B1>,U<B1>)?

There may be other places where the existence of a class definition, or worse, a template instantiation, changes the semantics of an otherwise valid program (e.g. pointer conversions?) but it seems like something to be avoided.

(See also issue 954.)

#### 1919. Overload resolution for `!` with explicit conversion operator

Section: 12.2.2.3 [over.match.oper] Status: open Submitter: Johannes Schaub Date: 2014-04-30

Although the intent is that the `!` operator should be usable with an operand that is a class object having an explicit conversion to `bool` (i.e., its operand is “contextually converted to `bool`”), the selection of the conversion operator is done via 12.2.2.3 [over.match.oper], 12.2.3 [over.match.viable], and 12.2.4 [over.match.best], which do not make specific allowance for this special characteristic of the `!` operator and thus will not select the explicit conversion function.

Notes from the June, 2014 meeting:

CWG noted that this same issue affects `&&` and `||`.

#### 2730. Comparison templates on enumeration types

Section: 12.2.2.3 [over.match.oper] Status: open Submitter: Tobias Loew Date: 2023-05-11

Equality and relational comparisons are supported for all enumeration types, even for scoped enumerations, as specified in 12.5 [over.built] paragraph 15 and 7.6.9 [expr.rel] paragraph 6:

> If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.

The comparisons can be overloaded using a non-template function, for example:

      enum class F {
        a, b
      };

      bool operator<(F,F) = delete;
      bool b = F::a < F::b;   // error, as intended

However, using a function template does not work:

      template<typename E>
      inline constexpr bool X = false;  // specialize to disable comparisons on E

      template <typename E> requires X<E>
      void operator<(E, E) = delete;

      template <>
      inline constexpr bool X<F> = true;

      bool b = F::a < F::b;             // ok, selects built-in comparison per 12.2.4.1 [over.match.best.general] bullet 2.4

The reason is that the built-in operator candidates are not suppressed if they compete with a template specialization.

Suggested resolution:

Change in 12.2.2.3 [over.match.oper] bullet 3.3.4 as follows:

> - do not have the same parameter-type-list as any non-member candidate or rewritten non-member candidate that is not a function template specialization.

#### 2804. Lookup for determining rewrite targets

Section: 12.2.2.3 [over.match.oper] Status: open Submitter: Richard Smith Date: 2023-10-13 Liaison: EWG

Consider:

      struct X {
        operator int();
        friend bool operator==(X, int);
        friend bool operator!=(X, int);  // #1
      } x;

      bool bx = x == x;    // error: lookup for rewrite target determination does not find hidden friend #1

      struct Y {
        operator int();
        friend bool operator==(Y, int);   // #2
      } y;

      bool operator!=(Y, int);            // #3

      bool by = y == y;                   // OK, #2 is not a rewrite target because lookup finds #3

A similar issue arises for function-scope declarations:

      struct X { operator int(); };

      bool f(X x) {
        bool operator==(X, int);
        return x == x;              // error
      }

      bool g(X x) {
        bool operator==(X, int);
        bool operator!=(X, int);
        return x == x;              // error
      }

      bool operator!=(X, int);

      bool h(X x) {
        bool operator==(X, int);
        return x == x;              // OK
      }

      bool i(X x) {
        bool operator==(X, int);
        bool operator!=(X, int);
        return x == x;              // OK
      }

Are these outcomes as intended? For the `bx` case at least, probably not.

CWG 2023-10-20

CWG seeks the advice of EWG whether fine-tuning of the "rewrite target" rules is desired, and what exactly those rules should be. See paper issue #1688.

EWG 2023-11-07

The intent is to check for the existence of a declaration that differs only in the operator name (ie, `bx` should work, `by` should be ill-formed).

This outcome also resolves issue 2797, but, more importantly, partially reverts P2468R2 (The Equality Operator You Are Looking For), applied in July 2022. In particular, the following example added thereby is no longer well-formed:

      struct B {
        bool operator==(const B&); // #2
      };
      struct C : B {
        C();
        C(B);
        bool operator!=(const B&); // #3
      };
      bool c1 = B() == C(); // OK, calls #2; reversed #2 is not a candidate because search for operator!= in C finds #3

Back to EWG for confirmation.

Additional notes (November, 2023)

P2468R2 also made the following example (derived from real-world code) ambiguous between the reversed built-in candicate and the user-provided `operator==`:

      template<class T>
      struct Ptr
      {
        bool operator==(T* p) const noexcept { return m_p == p; }
        operator T*() const noexcept { return m_p; }

        T* m_p;
      };

      void foo(Ptr<int> a, Ptr<int> b)
      {
        assert(a == b);
      }

EWG 2024-03-18

EWG invites a paper to propose a change.

#### 2844. Enumerating a finite set of built-in candidates

Section: 12.2.2.3 [over.match.oper] Status: open Submitter: Brian Bi Date: 2023-07-14

(From submission #362.)

Consider the following example, which is accepted by clang, but rejected by gcc:

      #include <concepts>

      struct S1 {
        operator int*() { return nullptr; }
      };

      struct S2 {
        template <class T>
        operator T() requires std::same_as<T, int*> {
          return nullptr;
        }
      };

      int main() {
        S1 s1;
        S2 s2;
        return s1 - s2;
      }

The question is whether the implementation is required to find the built-in candidate `std::ptrdiff_t operator-(int*, int*)`, and select that candidate. Subclause 12.5 [over.built] specifies an infinite number of built-in candidates, such as `std::ptrdiff_t operator-(T*, T*)` for every object type `T`. If there are infinitely many built-in candidates, the implementation cannot iterate through all of them to determine whether each candidate is viable.

The rule in 12.2.2.3 [over.match.oper] paragraph 3.3 is insufficient:

> - ...
> - ... For all other operators, the built-in candidates include all of the candidate operator functions defined in 12.5 [over.built] that, compared to the given operator,
>   - have the same operator name, and
>   - accept the same number of operands, and
>   - accept operand types to which the given operand or operands can be converted according to 12.2.4.2 [over.best.ics], and
>   - do not have the same parameter-type-list as any non-member candidate or rewritten non-member candidate that is not a function template specialization.
> - ...

Suggested resolution:

1. Add a new paragraph before 12.5 [over.built] paragraph 3 as follows:

   > For the purposes of this subclause, a type `T` is admissible for an operand E if a standard conversion sequence (12.2.4.2.2 [over.ics.scs]) exists from E to `T`. If E has a class type, then `T` is also admissible for E if E's class has a non-template conversion function `F` (11.4.8.3 [class.conv.fct]) that would be viable (12.2.3 [over.match.viable]) for a call of the form `(E).N()`, where N is a hypothetical id-expression that names F, and a standard conversion sequence to `T` exists from the type specified by `F`. If E denotes an overload set (12.3 [over.over]), then `T` is admissible for E if E contains any non-template function for which `T` is admissible.

   > In the remainder of this subclause, vq represents either `volatile` or no cv-qualifier.

2. Change in 12.5 [over.built] paragraph 4 through 6 as follows:

   > For every pair (T , vq), where T is a cv-unqualified arithmetic type other than bool or a cv-unqualified pointer to (possibly cv-qualified) object type, there exist candidate operator functions of the form

   >     vq T& operator++(vq T&);
   >     T operator++(vq T&, int);
   >     vq T& operator--(vq T&);
   >     T operator--(vq T&, int);

   > if `vq T&` is admissible for the operand.

   > For every (possibly cv-qualified) object type T and for every function type T that has neither cv-qualifiers nor a ref-qualifier, there exist candidate operator functions of the form

   >     T& operator*(T *);

   > if `T*` is admissible for the operand.

   > For every type T such that `T*` is admissible for the operand, there exist candidate operator functions of the form

   >     T* operator+(T *);

3. Change in 12.5 [over.built] paragraph 9 as follows:

   > For every quintuple (C1 , C2 , T , cv1, cv2 ), where C2 is a class type, C1 is the same type as C2 or is a derived class of C2 , and T is an object type or a function type, there exist candidate operator functions of the form

   >     cv12 T & operator->*(cv1 C1 *, cv2 T C2 ::*);

   > where cv12 is the union of cv1 and cv2, if `cv2 T C2::*` is admissible for the second operand. The return type is shown for exposition only; see 7.6.4 [expr.mptr.oper] for the determination of the operator's result type.

4. Change in 12.5 [over.built] paragraph 13 through 16 as follows:

   > For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form

   >     T* operator+(T *, std::ptrdiff_t);
   >     T& operator[](T *, std::ptrdiff_t);
   >     T* operator-(T *, std::ptrdiff_t);

   > if `T*` is admissible for the first operand.

   > For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form

   >     T* operator+(std::ptrdiff_t, T *);
   >     T& operator[](std::ptrdiff_t, T *);

   > if `T*` is admissible for the second operand.

   > For every T , where T is a pointer to object type and is admissible for the left or right operand, there exist candidate operator functions of the form

   >     std::ptrdiff_t operator-(T , T );

   > For every T, where T is an enumeration type, or a pointer type that is admissible for the left or right operand, there exist candidate operator functions of the form

   >     bool operator==(T , T );
   >     bool operator!=(T , T );
   >     bool operator<(T , T);
   >     bool operator>(T , T );
   >     bool operator<=(T , T );
   >     bool operator>=(T , T );
   >     R operator<=>(T , T );

   > where R is the result type specified in 7.6.8 [expr.spaceship].

   > For every T, where T is a pointer-to-member type and is admissible for the left or right operand, or T is `std::nullptr_t`, there exist candidate operator functions of the form

   >     bool operator==(T, T );
   >     bool operator!=(T , T );

5. Change in 12.5 [over.built] paragraph 19 through 21 as follows:

   > For every pair (T , vq), where T is any type, there exist candidate operator functions of the form

   >     T *vq & operator=(T *vq &, T *);

   > if `T vq&` is admissible for the left operand or `T*` is admissible for the right operand.

   > For every pair (T , vq), where T is an enumeration type, or `T` is a pointer-to-member type such that `vq T&` is admissible for the left operand or `T` is admissible for the right operand, there exist candidate operator functions of the form

   >     vq T & operator=(vq T &, T );

   > For every pair (T , vq), where T is a cv-qualified or cv-unqualified object type, there exist candidate operator functions of the form

   >     T *vq &  operator+=(T *vq &, std::ptrdiff_t);
   >     T *vq &  operator-=(T *vq &, std::ptrdiff_t);

   > if `T*vq&` is admissible for the left operand.

6. Change in 12.5 [over.built] paragraph 25 as follows:

   > For every type T, where T is a pointer, pointer-to-member, or scoped enumeration type,
   > - `T` is admissible for the second or third operand and is a pointer or pointer-to-member type, or
   > - `T` is a scoped enumeration type,
   > there exist candidate operator functions of the form

   >     operator?:(bool, T, T);

Additional notes (March, 2025)

The following cases where infinitely many candidates exist are not addressed:

    struct A { operator int(); };
    +A();    // if an implementation supports an unbounded set of extended integer types

(Also, the wording above does not change 12.5 [over.built] paragraph 10.)

    struct A { operator nullptr_t(); };
    A() + 0;   // nullptr_t has a standard conversion sequence to every pointer type

    struct A {};
    template<typename T> struct B : A {};
    using U = int A::*;
    struct C { operator U(); };
    C() == C();  // unbounded set of possible derived classes, when converting to int B<T>::* for any T

#### 2967. Explicit conversion functions

Section: 12.2.2.7 [over.match.ref] Status: open Submitter: Brian Bi Date: 2024-06-11

Consider:

      struct T {
        T(T&&);
      };

      struct U {
        explicit operator T&&();
      };

      int main() {
        T t(U{});
      }

To perform the direct-initialization of `t` using the move constructor of `T`, we have to find an implicit conversion sequence from `U` to `T&&`, which is covered by 9.5.4 [dcl.init.ref] bullet 5.3.2. The corresponding overload resolution in 12.2.2.7 [over.match.ref] paragraph 1 specifies:

> The permissible types for non-explicit conversion functions are the members of R where “cv1 T” is reference-compatible (9.5.4 [dcl.init.ref]) with “cv2 T2”. For direct-initialization, the permissible types for explicit conversion functions are the members of R where T2 can be converted to type T with a (possibly trivial) qualification conversion (7.3.6 [conv.qual]); otherwise there are none.

However, the reference in question is not direct-initialized (the first parameter of the constructor is copy-initialized), thus the explicit conversion function is not considered. However, all implementations agree that the example is valid.

Subclause 12.2.2.5 [over.match.copy] paragraph 1.2 already handles this case for passing values.

Suggested resolution:

Change in 12.2.2.7 [over.match.ref] paragraph 1 as follows:

> ... The permissible types for non-explicit conversion functions are the members of R where “cv1 T” is reference-compatible (9.5.4 [dcl.init.ref]) with “cv2 T2”. For direct-initialization, and for copy-initialization of the first parameter of a constructor where the constructor is called with a single argument in the context of direct-initialization of an object of type "cv3 T", the permissible types for explicit conversion functions are the members of R where T2 can be converted to type T with a (possibly trivial) qualification conversion (7.3.6 [conv.qual]); otherwise there are none.

#### 2311. Missed case for guaranteed copy elision

Section: 12.2.2.8 [over.match.list] Status: open Submitter: Richard Smith Date: 2016-08-09

Consider:

      struct X {
        X();
      };
      X make();
      X x{make()};

We reach 9.5.5 [dcl.init.list] bullet 3.7:

> Otherwise, if `T` is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (12.2 [over.match], 12.2.2.8 [over.match.list]).

This means we perform a redundant copy. If `T` were an aggregate, 9.5.5 [dcl.init.list] bullet 3.2 would avoid the redundant copy:

> If `T` is an aggregate class and the initializer list has a single element of type cv `U`, where `U` is `T` or a class derived from `T`, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

See also issues 2137 and 2327.

#### 2425. Confusing wording for deduction from a type

Section: 12.2.2.9 [over.match.class.deduct] Status: open Submitter: Dawn Perchik Date: 2019-08-06

In 12.2.2.9 [over.match.class.deduct] paragraph 3 we read:

> The arguments of a template `A` are said to be deducible from a type `T` if, given a class template

>       template <typename> class AA;

> with a single partial specialization whose template parameter list is that of `A` and whose template argument list is a specialization of `A` with the template argument list of `A` (13.8.3.2 [temp.dep.type]), AA<T> matches the partial specialization.

The relationship between `A`, `AA` and its partial specialization, and the argument list of A is not clear. An example would be very helpful here. Also, using a different name than `A` would help, since `A` is used in close proximity to this wording to denote an alias template, while this wording applies to both class and alias templates. Finally, there should be a cross-reference to 13.7.6.2 [temp.spec.partial.match] for matching the partial specialization.

#### 2680. Class template argument deduction for aggregates with designated initializers

Section: 12.2.2.9 [over.match.class.deduct] Status: open Submitter: Christof Meerwald Date: 2023-01-09

Consider:

       template<typename T>
       struct X
       {
        int i{};
        T t;
       };

       X x{ .t = X<int>{ 1, 2 } }; // should deduce X<X<int>> (not X<int>)

and

       template<typename T>
       struct Y
       {
        int i;
        T t;
        int j;
       };

       template<typename T>
       Y(int, T) -> Y<T>;

       Y y{ .t = 'c', .j = 2 }; // should be ill-formed

Class template argument deduction does not limit deduction to the aggregate deduction candidate when a designated initializer list is used.

Suggested resolution:

1. Change in 12.2.2.9 [over.match.class.deduct] paragraph 1 as follows:

   > ... If the initializer has a designated-initializer-list, any guide that is not an aggregate deduction candidate is not viable; for an aggregate deduction candidate, the designated-initializer-list is replaced by an initializer-list with the same elements. In addition, if C is defined and inherits constructors (9.10 [namespace.udecl]) from a direct base class ...

2. Change in 12.2.2.9 [over.match.class.deduct] bullet 3.5 as follows:

   > - ...
   > - If f was generated from a deduction-guide (13.7.2.3 [temp.deduct.guide]), then f' is considered to be so as well.
   > - If f was generated from an aggregate deduction guide, then f' is considered to be so as well.

#### 2735. List-initialization and conversions in overload resolution

Section: 12.2.4 [over.match.best] Status: open Submitter: Jason Merrill Date: 2023-03-23

There is implementation divergence in handling the following example:

      template <typename _Tp> struct optional {
        template <typename _Up> explicit optional(_Up);
        template <typename _Up = _Tp> void operator=(_Up);
      };
      struct SourceBrush {
        struct Brush {
         int brush;
        };
        void setPattern() { m_brush = {42}; }
        optional<Brush> m_brush;
      };

The following example is ambiguous per issue 1228:

      #include <unordered_set>
      #include <string>

      template<typename T> struct wrap {
        template<typename ...Ts> explicit wrap(Ts &&...args) : v(std::forward<Ts>(args)...) {}
        explicit wrap(const T &v) : v(v) {}
        explicit wrap(T &&v) : v(v) {}
        wrap(const wrap&) = delete;
        T v;
      };

      void f() {
        wrap<std::unordered_set<std::string>> wrapped_set({"foo", "bar", "baz"});
      }

The copy constructor of `wrap<...>` becomes viable, by way of constructing another `wrap` object from the given initializer list using the explicit constructor template. It looks like a deleted copy constructor is used to remove a level of braces, and then picking an explicit constructor to construct the source of the copy.

Another example:

      #include <string>
      #include <map>

      struct X {
        explicit X(const std::map<std::string, std::string> &map);
      };

      struct Y {
        Y() : x({{"foo", "bar"}}) {}
        X x;
      };

The intent is to construct the map with a single key-value pair, but the list-initialization is ambiguous with invoking the copy constructor and creating a map from a pair of iterators given by `{"foo", "bar"}`.

And another example:

      struct Z {};

      struct X {
        explicit X(const Z &z = {});
      };

      struct Y {
        Y() : x({}) {}
        X x;
      };

The ambiguity is between

- calling the `X(const Z&)` constructor with `{}` and
- calling the `X(const X&)` constructor and using the explicit default constructor to construct a temporary `X`.

Core issue 2267 is also related.

Suggested resolution:

Insert before 12.2.4.1 [over.match.best.general] bullet 2.4 as follows:

> - ...
> - `F2` is a copy or move special member function where the implicit conversion sequence for its (non-object) argument is a user-defined conversion sequence specified by a non-converting constructor (considered under 12.2.2.8 [over.match.list]) and `F1` is not, or, if not that, [ Example:

>         struct Z {};

>         struct X {
>           explicit X();            // #1
>           explicit X(const Z &z);  // #2
>         };

>         struct Y {
>           Y() : x({}) {}  // OK, calls #2, not ambiguous with the copy constructor of X using a temporary initialized by #1
>           X x;
>         };

>   -- end example ]
> - F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
> - ...

#### 2525. Incorrect definition of implicit conversion sequence

Section: 12.2.4.2.1 [over.best.ics.general] Status: open 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.5 [dcl.init], 9.5.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 [conv.general] 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.5 [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 A{
        A(int) = delete;
      };
      struct B{
         B(int) {}
      };
      void fun(A); // #1
      void fun(B); // #2
      int main() {
        fun(0);    // #3
      }

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

      A t = 1;

would be ill-formed.

Proposed resolution (approved by CWG 2022-12-02):

Change 12.2.4.2.1 [over.best.ics.general] 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.5 [dcl.init], 9.5.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.

#### 2679. Implicit conversion sequence with a null pointer constant

Section: 12.2.4.2.1 [over.best.ics.general] Status: open Submitter: Lénárd Szolnoki Date: 2023-01-07

Subclause 12.2.4.2.1 [over.best.ics.general] paragraph 1, as modified by issue 2525, claims that only the type, cv-qualification, and value category of the argument are relevant in forming an implicit conversion sequence. This is inaccurate for the following situations:

- A literal `0` can be implicitly converted to a parameter of type `void*` (and in practice, an implicit conversion sequence does exist), yet the literal `0` has the same type, cv-qualification, and value category as the literal `1` despite no such implicit conversion being allowed for the latter.
- An initializer list does not have a type, cv-qualification, or value category at all.
- An overload set does not have a single (function) type.

#### 2829. Redundant case in restricting user-defined conversion sequences

Section: 12.2.4.2.1 [over.best.ics.general] Status: open Submitter: Krystian Stasiowski Date: 2020-03-13

(From editorial issue 3871.)

Subclause 12.2.4.2.1 [over.best.ics.general] paragraph 4 specifies:

> However, if the target is
> - the first parameter of a constructor or
> - ...
> and the constructor ... is a candidate by
> - 12.2.2.4 [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization,
> - 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] (in all cases), or
> - ...
> user-defined conversion sequences are not considered.

A temporary is no longer created in 9.5.1 [dcl.init.general] bullet 16.6.3 "for the remaining copy-initialization cases". Instead, that section directly branches to 12.2.2.5 [over.match.copy], which is covered by the fourth bullet above.

Suggested resolution:

Change in 12.2.4.2.1 [over.best.ics.general] paragraph 4 as follows:

> However, if the target is
> - ...
> and the constructor ... is a candidate by
> - 12.2.2.4 [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization,
> - 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] (in all cases), or
> - ...
> user-defined conversion sequences are not considered.

#### 2731. List-initialization sequence with a user-defined conversion

Section: 12.2.4.2.3 [over.ics.user] Status: open Submitter: Brian Bi Date: 2023-05-11

Consider:

      struct C {
        C(int);
      };

      struct A {
        A(C);
      };

      struct B {
        B(C);
      };

      int f(const A&);
      int f(B&&);

      int x = f({1});

Subclause 12.2.4.2.3 [over.ics.user] paragraph 1 specifies:

> A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (11.4.8 [class.conv]) followed by a second standard conversion sequence. If the user-defined conversion is specified by a constructor (11.4.8.2 [class.conv.ctor]), the initial standard conversion sequence converts the source type to the type of the first parameter of that constructor. ...

However, there is no "source type" for an initializer list, making the treatment of the example unclear.

Suggested resolution:

Change in 12.2.4.2.6 [over.ics.list] bullet 7.2 as follows:

> - ...
> - Otherwise, the implicit conversion sequence is a user-defined conversion sequence whose user-defined conversion is specified by the constructor `C` and whose first and second standard conversion sequence is an sequences are identity conversion conversions.

#### 3057. Ranking of derived-to-base conversions should ignore reference binding

Section: 12.2.4.2.5 [over.ics.ref] Status: open Submitter: Evan Girardin Date: 2025-07-31

(From submission #733.)

The derived-to-base tiebreaker rules in 12.2.4.3 [over.ics.rank] paragraph 4 should apply regardless of whether one destination type is a reference and the other one is not. Major implementations agree.

Suggested resolution:

1. Change in 12.2.4.2.5 [over.ics.ref] paragraph 1 as follows:

   > When a parameter of type “reference to cv T” binds directly (9.5.4 [dcl.init.ref]) to an argument expression:
   > - If the argument expression has a type `D` that is a derived class of the parameter type `T`, the implicit conversion sequence is a derived-to-base conversion from `D` to `T` (12.2.4.2 [over.best.ics]).
   > - ...

2. Change in 12.2.4.3 [over.ics.rank] bullet 4.5 as follows:

   > - ...
   > - If class B is derived directly or indirectly from class A and class C is derived directly or indirectly from B,
   >   - conversion of C* to B* is better than conversion of C* to A*, [Example 10: ... —end example]
   >   - binding of an expression of type C to a reference to type B is better than binding an expression of type C to a reference to type A,
   >   - conversion of A::* to B::* is better than conversion of A::* to C::*,
   >   - conversion of C to B is better than conversion of C to A,
   >   - conversion of B* to A* is better than conversion of C* to A*,
   >   - binding of an expression of type B to a reference to type A is better than binding an expression of type C to a reference to type A,
   >   - conversion of B::* to C::* is better than conversion of A::* to C::*, and
   >   - conversion of B to A is better than conversion of C to A.
   >   [ Note: ... ]

#### 2169. Narrowing conversions and overload resolution

Section: 12.2.4.2.6 [over.ics.list] Status: open Submitter: David Krauss Date: 2015-08-26

Current implementations ignore narrowing conversions during overload resolution, emitting a diagnostic if calling the selected function would involve narrowing. For example:

      struct s { long m };
      struct ss { short m; };

      void f( ss );
      void f( s );
      void g() {
        f({ 1000000 }); // Ambiguous in spite of narrowing for f(ss)
      }

However, the current wording of 12.2.4.2.6 [over.ics.list] paragraph 7 says,

> Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization (9.5.2 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion.

In the example above, `ss` cannot be initialized from `{ 1000000 }` because of the narrowing conversion, so presumably `f(ss)` should not be considered. If this is not the intended outcome, paragraph 7 should be restated in terms of having an implicit conversion sequence, as in, e.g., bullet 9.1, instead of a valid initialization.

Rationale (March, 2016):

This is a question of language design and thus more suited to consideration by EWG.

EWG (January, 2021):

Adjust the standard to follow existing implementations. See vote.

Additional notes (March, 2023)

A narrowing conversion is but one of the many circumstances that could make a conversion ill-formed despite the existence of an implicit conversion sequence. Other examples (with implementation divergence) are invoking a private or deleted function, binding a non-const reference to a bit-field, invoking an explicit constructor in list-initialization, invoking a consteval constructor with unsuitable arguments, or initializing an aggregate with incorrectly-ordered designated initializers. A resolution of this issue should thus not be focused on narrow conversions.

#### 2492. Comparing user-defined conversion sequences in list-initialization

Section: 12.2.4.2.6 [over.ics.list] Status: open Submitter: Jim X Date: 2021-01-11

Consider the following example:

      #include <initializer_list>
      struct A{
        operator short(){
          return 0;
        }
      };
      struct B{
        operator bool(){
          return 0;
        }
      };
      void fun(std::initializer_list<int>){}
      void fun(std::initializer_list<bool>){}
      int main(){
        fun({A{},B{}});
      }

According to 12.2.4.2.6 [over.ics.list] paragraph 6,

> Otherwise, if the parameter type is `std::initializer_list<X>` 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<X>` 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:

>       void f(std::initializer_list<int>);
>       f( {} );        // OK: f(initializer_list<int>) identity conversion
>       f( {1,2,3} );   // OK: f(initializer_list<int>) identity conversion
>       f( {'a','b'} ); // OK: f(initializer_list<int>) integral promotion
>       f( {1.0} );     // error: narrowing

>       struct A {
>         A(std::initializer_list<double>);            // #1
>         A(std::initializer_list<complex<double>>);   // #2
>         A(std::initializer_list<std::string>);       // #3
>       };
>       A a{ 1.0,2.0 };        // OK, uses #1

>       void g(A);
>       g({ "foo", "bar" });   // OK, uses #3

>       typedef int IA[3];
>       void h(const IA&);
>       h({ 1, 2, 3 });        // OK: identity conversion

>       void x(std::initializer_list<int>);
>       void x(std::initializer_list<bool>);
>       struct S1 { operator short(); };
>       struct S2 { operator bool(); };
>       void y() {
>         x({S1{}, S2{}});   // error: ambiguous. The ICSes for each list element are indistinguishable because
>                            // they do not contain the same conversion function, so the worst conversion is
>                            // the ambiguous conversion sequence.
>       }

> —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<X>` 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.

#### 2741. Implicit conversion sequence from empty list to array of unknown bound

Section: 12.2.4.2.6 [over.ics.list] Status: open Submitter: Richard Smith Date: 2023-06-01

Consider:

      int f(int const(&)[]) { return 1; }   // #1
      int f(int const(&)[2]) { return 2; }  // #2
      int x = f({});

Subclause 12.2.4.2.6 [over.ics.list] paragraph 6 specifies:

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

What is the "worst [...] implicit conversion sequence" when the initializer list is empty? Should #1 even be viable for this call, given that it's not valid to initialize an `int const (&)[]` from `{}`?

There is implementation divergence: Clang and MSVC treat #1 as being non-viable; GCC calls #1 and creates a temporary of type `int[0]` (even in -pedantic-errors mode). Should the "worst conversion sequence" over an empty set be the identity conversion, selecting #1 and then failing the parameter initialization?

#### 2790. Aggregate initialization and user-defined conversion sequence

Section: 12.2.4.2.6 [over.ics.list] Status: open Submitter: Johannes Schaub Date: 2023-08-18

Subclause 12.2.4.2.6 [over.ics.list] paragraph 8 specifies:

> Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization (9.5.2 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.

However, a user-defined conversion sequence cannot contain an aggregate initialization per 12.2.4.2.3 [over.ics.user] paragraph 1:

> A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (11.4.8 [class.conv]) followed by a second standard conversion sequence.

To complete the argument, subclause 11.4.8 [class.conv] paragraph 1 specifies:

> Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions...

Possible resolution:

Change in 12.2.4.2.3 [over.ics.user] paragraph 1 as follows:

> A user-defined conversion sequence consists of an initial standard conversion sequence, followed by a user-defined conversion (11.4.8 [class.conv]) or an aggregate initialization (9.5.2 [dcl.init.aggr]), followed by a second standard conversion sequence.

#### 1459. Reference-binding tiebreakers in overload resolution

Section: 12.2.4.3 [over.ics.rank] Status: open Submitter: Jason Merrill Date: 2012-02-07

Both paragraph 3 and paragraph 4 of 12.2.4.3 [over.ics.rank] have overload resolution tiebreakers for reference binding. It might be possible to merge those into a single treatment.

#### 1789. Array reference vs array decay in overload resolution

Section: 12.2.4.3 [over.ics.rank] Status: open Submitter: Faisal Vali Date: 2013-10-01

The current rules make an example like

      template<class T, size_t N> void foo(T (&)[N]);
      template<class T> void foo(T *t);

      int arr[3]{1, 2, 3};
      foo(arr);

ambiguous, even though the first is an identity match and the second requires an lvalue transformation. Is this desirable?

Proposed resolution (June, 2021):

Add the following as a new bullet following 12.2.4.3 [over.ics.rank] bullet 3.2.6:

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

> - List-initialization sequence `L1` is a better conversion sequence...

> - Standard conversion sequence `S1` is a better conversion sequence than standard conversion sequence `S2` if

> - ...

> - `S1` and `S2` include reference bindings (9.5.4 [dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by `S2` refers is more cv-qualified than the type to which the reference initialized by `S1` refers. [Example 6:...

> - `S1` is a reference binding to an array and `S2` is an array-to-pointer conversion (7.3.3 [conv.array]). [Example 7:

>         template<class T, unsigned N> void f(T (&)[N]); // #1
>         template<class T> void f(T *t);                 // #2
>         int r[3]{1, 2, 3};
>         void g() {
>           f(r);    // OK: calls #1
>         }

>   —end example]

CWG 2023-02-06

The proposed resolution prefers the `T&` overload over the `T*` overload in the example below, which is undesirable. The wording needs to be amended to limit the tiebreaker to situations where the array declarator appears in the signature.

>     template<class T>
>     int f(T&);
>     template<class T>
>     int f(T*);
>     int x[5];
>     int z = f(x);

#### 2337. Incorrect implication of logic ladder for conversion sequence tiebreakers

Section: 12.2.4.3 [over.ics.rank] Status: open Submitter: Richard Smith Date: 2017-03-02

The bulleted list of 12.2.4.3 [over.ics.rank] paragraph 3 consists of a logic ladder of the form “A is better than B if [some predicate relating A to B], or, if not that, ...” For example, bullet 3.1 says,

- List-initialization sequence `L1` is a better conversion sequence than list-initialization sequence `L2` if

- `L1` converts to `std::initializer_list<X>` for some `X` and `L2` does not, or, if not that,

- `L1` converts to type “array of `N1 T`”, `L2` converts to type “array of `N2 T`”, and `N1` is smaller than `N2` ,

The intent is not to fall into the array case if `L2` converts to `std::initializer_list<X>` and `L1` does not — i.e., the inverse predicate holds — but that intent is not well reflected in the actual wording.

#### 2842. Preferring an `initializer_list` over a single value

Section: 12.2.4.3 [over.ics.rank] Status: open Submitter: Jason Merrill Date: 2022-12-10

Consider:

      #include <initializer_list>
      struct A { A(int) {} };
      void f(int) = delete;
      void f(std::initializer_list<A>) {}   // #1
      int main() { f({10}); }

The intent of issue 1467 was to make this example well-formed by choosing #1, according to this rule and a corresponding example in 12.2.4.3 [over.ics.rank] bullet 3.1:

> - List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if
>   - L1 converts to std::initializer_list<X> for some X and L2 does not, or, if not that,
>   - ...
>   even if one of the other rules in this paragraph would otherwise apply.
> - ...

See also issue 2137 and gcc issue 64665.

Possible resolution:

Move 12.2.4.3 [over.ics.rank] paragraph 2 into paragraph 3 as follows:

> When comparing the basic forms of implicit conversion sequences (as defined in 12.2.4.2 [over.best.ics])

> - a standard conversion sequence (12.2.4.2.2 [over.ics.scs]) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
> - a user-defined conversion sequence (12.2.4.2.3 [over.ics.user]) is a better conversion sequence than an ellipsis conversion sequence (12.2.4.2.4 [over.ics.ellipsis]).

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

> - List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if
>   - L1 converts to std::initializer_list<X> for some X and L2 does not, or, if not that,
>   - ...
>   even if one of the other rules in this paragraph would otherwise apply.
> - Otherwise, a standard conversion sequence (12.2.4.2.2 [over.ics.scs]) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
> - a user-defined conversion sequence (12.2.4.2.3 [over.ics.user]) is a better conversion sequence than an ellipsis conversion sequence (12.2.4.2.4 [over.ics.ellipsis]).
> - Standard conversion sequence S1 is a better conversion sequence than ...

#### 2958. Overload resolution involving lvalue transformation and qualification conversion

Section: 12.2.4.3 [over.ics.rank] Status: open Submitter: Vincent X Date: 2024-11-10

(From submission #639.)

The following example in 12.2.4.3 [over.ics.rank] bullet 3.2.5 is not in harmony with the normative rules:

      int g(const int*);         //#1
      int g(const volatile int* const&); //#3
      int* p;
      int k = g(p);          // calls g(const int*)

The invocation of #1 requires an lvalue-to-rvalue conversion followed by a qualification conversion; #2 requires only a qualification conversion.

Possible resolution:

Change in 12.2.4.3 [over.ics.rank] bullet 3.2.5 as follows:

> - ...
> - S1 and S2 differ only in their qualification conversion (7.3.6 [conv.qual]), ignoring any Lvalue Transformation, and yield similar types T1 and T2, respectively (where a standard conversion sequence that is a reference binding is considered to yield the cv-unqualified referenced type), where T1 and T2 are not the same type, and const T2 is reference-compatible with T1 (9.5.4 [dcl.init.ref]) ...

#### 2873. Taking the address of a function involving template argument deduction

Section: 12.3 [over.over] Status: open Submitter: Anoop Rana Date: 2024-03-16

(From submission #516.)

Consider:

      template <typename T>
      int f(T&&);

      void g();                      // #1

      template <typename T>
      void g();                      // #2

      int x = f(&g);

All major implementations reject. However, 12.3 [over.over] paragraph 3 seems to say that templates are ignored if template argument deduction fails ("if any"):

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

For the following example, prior core issues 2608 and 2848 have indicated that "deduction" should always consider default template arguments. Yet, only gcc accepts the example.

      template<typename T> int f(T);
      template<typename T = int> void g();
      int x = f(&g);

See also issue 2572.

Subclause 13.10.3.6 [temp.deduct.type] bullet 5.6.3 makes the parameter of `f` a non-deduced context for these situations. However, 13.10.2 [temp.arg.explicit] paragraph 4 allows omitting the template argument list if all template parameters can be deduced or obtained from default template arguments. Thus, `&g` can be considered to refer to both a function template and a function template specialization.

The special treatment in overload resolution for `f` yields inconsistent outcomes where overload resolution is not applied:

      void g(int);
      void g(auto);

      decltype(&g) p;    // rejected by implementations for no reason in the wording

#### 1549. Overloaded comma operator with `void` operand

Section: 12.4.3 [over.binary] Status: open Submitter: Nikolay Ivchenkov Date: 2012-09-04

Even though a function cannot take a parameter of type `void`, the current rules for overload resolution require consideration of overloaded operators when one operand has a user-defined or enumeration type and the other has type `void`. This can result in side effects and possibly errors, for example:

      template <class T> struct A {
        T t;
        typedef T type;
      };

      struct X {
        typedef A<void> type;
      };

      template <class T> void operator ,(typename T::type::type, T) {}

      int main() {
        X(), void(); // OK
        void(), X(); // error: A<void> is instantiated with a field of
                     // type void
      }

#### 260. User-defined conversions and built-in `operator=`

Section: 12.5 [over.built] Status: open Submitter: Scott Douglas Date: 4 Nov 2000

According to the Standard (although not implemented this way in most implementations), the following code exhibits non-intuitive behavior:

      struct T {
        operator short() const;
        operator int() const;
      };

      short s;

      void f(const T& t) {
        s = t;  // surprisingly calls T::operator int() const
      }

The reason for this choice is 12.5 [over.built] paragraph 18:

> For every triple (L, VQ, R), where L is an arithmetic type, VQ is either `volatile` or empty, and R is a promoted arithmetic type, there exist candidate operator functions of the form

> VQ L`& operator=(`VQ L`&,` R`);`

Because R is a "promoted arithmetic type," the second argument to the built-in assignment operator is `int`, causing the unexpected choice of conversion function.

Suggested resolution: Provide built-in assignment operators for the unpromoted arithmetic types.

Related to the preceding, but not resolved by the suggested resolution, is the following problem. Given:

        struct T {
    	 operator int() const;
    	 operator double() const;
        };

I believe the standard requires the following assignment to be ambiguous (even though I expect that would surprise the user):

        double x;
        void f(const T& t) { x = t; }

The problem is that both of these built-in `operator=()`s exist (12.5 [over.built] paragraph 18):

        double& operator=(double&, int);
        double& operator=(double&, double);

Both are an exact match on the first argument and a user conversion on the second. There is no rule that says one is a better match than the other.

The compilers that I have tried (even in their strictest setting) do not give a peep. I think they are not following the standard. They pick `double& operator=(double&, double)` and use `T::operator double() const`.

I hesitate to suggest changes to overload resolution, but a possible resolution might be to introduce a rule that, for built-in `operator=` only, also considers the conversion sequence from the second to the first type. This would also resolve the earlier question.

It would still leave `x += t` etc. ambiguous -- which might be the desired behavior and is the current behavior of some compilers.

Notes from the 04/01 meeting:

The difference between initialization and assignment is disturbing. On the other hand, promotion is ubiquitous in the language, and this is the beginning of a very slippery slope (as the second report above demonstrates).

Additional note (August, 2010):

See issue 507 for a similar example involving comparison operators.

#### 954. Overload resolution of conversion operator templates with built-in types

Section: 12.5 [over.built] Status: open Submitter: Steve Clamage Date: 19 August, 2009

Consider the following example:

        struct NullClass {
            template<typename T> operator T () { return 0 ; }
        };

        int main() {
            NullClass n;
            n==5;        // #1
            return 0;
        }

The comparison at #1 is, according to the current Standard, ambiguous. According to 12.5 [over.built] paragraph 12, the candidates for `operator==(L, R)` include functions “for every pair of promoted arithmetic types,” so `L` could be either `int` or `long`, and the conversion operator template will provide an exact match for either.

Some implementations unambiguously choose the `int` candidate. Perhaps the overload resolution rules could be tweaked to prefer candidates in which `L` and `R` are the same type?

(See also issue 545.)

#### 1620. User-defined literals and extended integer types

Section: 12.6 [over.literal] Status: open Submitter: Jason Merrill Date: 2013-02-12

Although numeric literals can have extended integer types, user-defined literal operators cannot have a parameter of an extended integer type. This seems like an oversight.

#### 2904. Introducing template-names

Section: 13.1 [temp.pre] Status: open Submitter: Brian Bi Date: 2024-06-14

A template-declaration should, but does not, introduce the identifier in its declarator-id as a template-name. Subclause 13.2 [temp.param] paragraph 3 already does so for template template-parameters.

Possible resolution (based on issue 2862):

1. Change in 9.2.4 [dcl.typedef] paragraph 2 as follows:

   > A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword is not looked up; if the alias-declaration does not introduce an alias template (13.1 [temp.pre]), the identifier it becomes a typedef-name and the. The optional attribute-specifier-seq following the identifier appertains to that typedef-name or, if the identifier is a template-name, to that template-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type.

2. Change in 11.1 [class.pre] paragraph 1 as follows:

   > A class is a type. Its name becomes a class-name (11.3 [class.name]) within its scope.

   >       class-name :
   >           identifier
   >           simple-template-id

   > ... Otherwise, the class-name is an identifier; it is not looked up, and if the class-specifier does not introduce a class template (13.1 [temp.pre]), the class-specifier introduces it the identifier as a class-name.

3. Change in 13.1 [temp.pre] paragraph 3 as follows:

   > - A class template is introduced by a template-declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or consists solely of an elaborated-type-specifier.
   > - A function template is introduced by a template-declaration whose declaration declares a function.
   > - An alias template is introduced by a template-declaration whose declaration is an alias-declaration.
   > - A variable template is introduced by a template-declaration whose declaration declares a variable.
   > The declaration of a class template, alias template, or variable template introduces its respective name as a template-name.

4. Change in 13.7.8 [temp.alias] paragraph 1 as follows:

   > A template-declaration in which the declaration is an alias-declaration (9.1 [dcl.pre]) declares the identifier to be an alias template. An alias template is a name for a family of types. The name of the alias template is a template-name.

5. Change in 13.7.9 [temp.concept] paragraph 2 as follows:

   > A concept-definition declares a concept. Its identifier becomes a template-name and a concept-name referring to that concept within its scope. The optional attribute-specifier-seq appertains to the concept.

6. Change in 13.3 [temp.names] paragraph 3 as follows:

   > [ Note: If the name is an identifier, it is then interpreted as a template-name. [ Note: The keyword `template` is used to indicate that a dependent qualified name (13.8.3.2 [temp.dep.type]) denotes a template where an expression might appear. —end note]

#### 2994. Allowing template parameters following template parameter packs that are pack expansions

Section: 13.2 [temp.param] Status: open Submitter: Jan Schultke Date: 2025-01-20

(From submission #666.)

Consider:

      template <class Head, class... Tail>
      struct test {
        template <Tail..., Head>
        static void make() {}       // clang accepts, MSVC rejects
      };

      int main() {
        test<char, int, float>::make<4, 6.f, 'a'>();
      }

There is implementation divergence for the treatment of this example. It ought to be allowed.

Suggested resolution:

Change in 13.2 [temp.param] paragraph 14 as follows:

> ... If a A template-parameter pack of a function template declares a template parameter pack, it shall not be followed by another template-parameter P unless
> - that template parameter P is deducible from the parameter-type-list (9.3.4.6 [dcl.fct]) of the function template or ,
> - P has a default argument (13.10.3 [temp.deduct]), or
> - that template parameter pack is also a pack expansion.
> ...

#### 579. What is a “nested” `>` or `>>`?

Section: 13.3 [temp.names] Status: open Submitter: Daveed Vandevoorde Date: 11 May 2006

The Standard does not normatively define which `>` and `>>` tokens are to be taken as closing a template-argument-list; instead, 13.3 [temp.names] paragraph 3 uses the undefined and imprecise term “non-nested:”

> When parsing a template-id, the first non-nested `>` is taken as the end of the template-argument-list rather than a greater-than operator. Similarly, the first non-nested `>>` is treated as two consecutive but distinct `>` tokens, the first of which is taken as the end of the template-argument-list and completes the template-id.

The (non-normative) footnote clarifies that

> A `>` that encloses the type-id of a `dynamic_cast`, `static_cast`, `reinterpret_cast` or `const_cast`, or which encloses the template-arguments of a subsequent template-id, is considered nested for the purpose of this description.

Aside from the questionable wording of this footnote (e.g., in what sense does a single terminating character “enclose” anything, and is a nested template-id “subsequent?”) and the fact that it is non-normative, it does not provide a complete definition of what “nesting” is intended to mean. For example, is the first `>` in this putative template-id “nested” or not?

        X<a ? b > c : d>

Additional note (January, 2014):

A similar problem exists for an `operator>` template:

      struct S;
      template<void (*)(S, S)> struct X {};
      void operator>(S, S);
      X<operator> > x;

Somehow the specification must be written to avoid taking the `>` token in the operator name as the end of the template argument list for `X`.

#### 2920. The `template` keyword for base classes

Section: 13.3 [temp.names] Status: open Submitter: Corentin Jabot Date: 2024-07-15

Consider:

      template<int T>
      struct BaseT {
        void Foo() {}
      };

      template<int T>
      struct DerivedT : BaseT<T> {
        void Inner() {
          this->template /* unnecessary keyword before CWG1835 */ BaseT<T>::Foo();
        }
      };

The resolution of issue 1835 in P1787 now requires the `template` keyword in this situation. This breaks code in several large projects.

The reason to require the `template` keyword is that the injected-class-name, like any other member, is inherited from the base class, but the base class is dependent, and thus name lookup does not consider base class members when parsing the template definition. For example:

      namespace N
      {
        template<int I>
        struct A { };

        template<int I>
        struct B
        {
          using C = A<I>;

          template<int J>
          using D = A<J>;
        };
      }

      // impossible to determine the injected-class-name of the base class in the definition context since 'N::B' could be specialized.
      // it might not be the injected-class-name of a template, and it might not even be 'C'/'D'!

      template<int I>
      struct X : N::B<I>::C { };

      template<int I>
      struct Y : N::B<I>::template D<I> { };

Even if we specified how to find the injected-class-name of a dependent base class for a subset of cases, we would reintroduce the problem that the resolution to issue 1835 solves:

      template<int I>
      struct A
      {
        int A;
      };

      bool f();

      template<int I>
      struct B : A<I>
      {
        bool g()
        {
          return this->A < I; // do we interpret '<' as the start of a template-argument-list here?
        }

        bool h()
        {
          return this->A < I > ::f(); // how about here?
        }
      };

Using the unqualified lookup results from the template definition context for disambiguation when the object expression is dependent is also wrong since they are discarded if we find a member in the template instantiation context. Consider:

      namespace N
      {
        template<int I>
        struct A
        {
          int B;
        };
      }

      template<int I>
      using B = N::A<I>;

      template<int I>
      struct C : B<I>
      {
        bool f()
        {
          return this->B < I;
        }
      };

Since the lookup context of `B` in `this->B` is dependent, we cannot determine whether it would be invalid as a complete class member access expression. We also cannot parse past the '<' to determine whether `B` is the terminal name of a member-qualified `nested-name-specifier`, so we do not know if unqualified name lookup should happen. Issue 1835 resolves this by treating `this->B` as a complete class member access expression. Using the results of unqualified name lookup to determine whether `B` is the name of a template is incorrect because it may refer to a non-template member when `C` is instantiated.

CWG 2024-08-16

The resolution for issue 1835 in P1787 has addressed real concerns. CWG recognizes that real-world code now no longer compiles, although the fix for the affected source code is trivial. A limited exception to support some of the existing code might be feasible. CWG solicits a paper with specification and analysis.

#### 2980. Constraints on template template parameters

Section: 13.3 [temp.names] Status: open Submitter: Hubert Tong Date: 2024-12-23

(From submission #658.)

Subclause 13.3 [temp.names] paragraph 8 specifies:

> When the template-name of a simple-template-id names a constrained non-function template or a constrained template template-parameter , and all template-arguments in the simple-template-id are non-dependent (13.8.3.7 [temp.dep.temp]), the associated constraints (13.5.3 [temp.constr.decl]) of the constrained template shall be satisfied (13.5.2 [temp.constr.constr]).

For template template parameters, it is unclear how this provision interacts with instantiation or substitution. In particular, 13.4.4 [temp.arg.template] paragraph 3 allows for the template template parameter to be more constrained than the argument provided; however, once the parameter is replaced by the corresponding argument, the constraints of the parameter seemingly disappear. Consider:

      template <typename T>
      concept C = false;

      template <typename> struct Q;

      template <template <C> class TT>
      struct A {
        template <typename T> void f(TT<T> *) {}
      };

      template void A<Q>::f(Q<int> *);

Implementations accept this example. For the reverse situation, where 13.4.4 [temp.arg.template] paragraph 3 allows `P` to be unconstrained, also consider:

      template <typename T>
      concept C = false;

      template <typename> struct Q;

      template <template <typename> class TT, typename T>
      using Alias = TT<T>;

      template <template <C> class TT>
      struct A {
        Alias<TT, int> *p;
      };

      A<Q> a;

Implementations (other than gcc and clang, which seem not to implement the basic feature at all) appear to use the constraints of `TT` from `A` despite the simple-template-id emanating from `Alias`.

Maybe the constraints from template template parameters should be checked where they appear (including within an alias template) --- or are substituted into an alias template --- to form a simple-template-id once the arguments are known. That is, the associated constraints of both

- template template parameters of alias templates and
- template template parameters used as arguments to alias templates

are checked for simple-template-ids appearing in an alias template.

Also consider:

      template <typename T>
      concept C = true;

      template <typename T>
      concept CandMore = C<T> && true;

      template <typename> struct Q;

      template <template <C> class P, typename T>
      using Alias = P<T>;

      template <template <CandMore> class TT>
      struct A {
        Alias<TT, int> *p;  // #1
      };

      A<Q> a;

The `P` for `Alias` is not at least as specialized as the argument `TT` given at #1, if we take the `TT` constraints as written. However, the actual argument for `TT` may be such that `P` is at least as specialized as the argument `TT`. There is implementation divergence.

#### 2105. When do the arguments for a parameter pack end?

Section: 13.4 [temp.arg] Status: open Submitter: Hubert Tong Date: 2015-03-17

There does not appear to be a clear statement in the Standard that the first template parameter pack in a template parameter list corresponds to all remaining arguments in the template argument list. For example:

      template <int> struct A;

      template <int ...N, typename T> void foo(A<N> *..., T);
      void bar() {
       foo<0>(0, 0);      // okay: N consists of one template parameter, 0. T is deduced to int
       foo<0, int>(0, 0); // error: int does not match the form of the corresponding parameter N
      }

See also issue 2055.

Notes from the February, 2016 meeting:

The comments in the example reflect the intent.

#### 3032. Template argument disambiguation

Section: 13.4.1 [temp.arg.general] Status: open Submitter: Hubert Tong Date: 2025-02-03

(From submission #669.)

For the template-argument grammar in 13.3 [temp.names], there is a parsing ambiguity introduced by P2841R7 between nested-name-specifieropt template-name and type-id (via simple-type-specifier) naming a placeholder for deduced class type (9.2.9.8 [dcl.type.class.deduct]). For example:

      void g();

      template <typename T>
      struct A {
        static void f() {
          g<T::TT>((A *)0);
        }
      };

      template <typename T> void g(void *);
      template <auto> void g(void *);
      template <template <typename> class> void g(void *);

      struct Expr { enum { TT = 0 }; };
      struct Type { using TT = int; };
      struct Tmpl { template <typename> struct TT; };
      void h() {
        A<Expr>::f(); // all accept
        A<Type>::f(); // EDG, MSVC accept; GCC, Clang rejects
        A<Tmpl>::f(); // EDG, MSVC accept; GCC, Clang rejects
      }

P1787R6 established the direction that the `template` disambiguator is needed only when introducing a template-argument-list (13.3 [temp.names] paragraph 6). (Reflection made this assumption false.) All examples should be accepted.

See 13.8.2 [temp.local] for the use of an injected-class-name as a template argument.

Example:

      void g();

      template <typename T>
      struct A {
        static void f() {
          g<A>((A *)0);     // all accept
          g<A>((A *)0, 0);  // Clang, GCC, EDG accept; MSVC rejects
        }
      };

      template <typename T> void g(void *);
      template <template <typename> class> void g(void *, int);

      void h() { A<int>::f(); }

Possible resolution:

Strategy: Introduce a new grammar production akin reflection-name, as a preferred option for template-argument, and say what it means in the various cases.

1. Change in 13.3 [temp.names] paragraph 1 as follows:

   >     template-argument:
   >        template-argument-name
   >        constant-expression
   >        type-id
   >        nested-name-specifieropt template-name
   >        nested-name-specifier template template-name

   >     template-argument-name:
   >        nested-name-specifieropt identifier
   >        nested-name-specifier template identifier

2. Insert a new paragraph before 13.3 [temp.names] paragraph 7 as follows:

   > The component names of a template-argument-name are those of its nested-name-specifier (if any) and its identifier. The terminal name of a template-argument-name of the form nested-name-specifier `template` identifier shall denote a template.

   > A template-id is valid if ...

3. Insert a new paragraph before 13.4.1 [temp.arg.general] paragraph 3 as follows:

   > If a template-argument A matches the form template-argument-name, it is interpreted as such; the identifier is looked up and its meaning is determined as follows:

   > - If lookup finds an injected-class-name (13.8.2 [temp.local]), then:
   >   - If A is for a type template template parameter, A denotes the corresponding class template.
   >   - Otherwise, it denotes a type-name.
   > - Otherwise, if lookup finds a template, A denotes that template.
   > - Otherwise, if lookup finds a type alias or a type, A denotes the underlying type and is interpreted as a type-id.
   > - Otherwise, A shall be a constant-expression.

   > In a template-argument, an ambiguity between a type-id and an expression is resolved to a type-id, regardless of the form of the corresponding template-parameter...

#### 2961. Checking of ill-formed types in constraint-expressions

Section: 13.5 [temp.constr] Status: open Submitter: CWG Date: 2024-11-18

There is implementation divergence in the handling of the following example:

      template <typename T>
      concept C = true;

      template <typename T>
      void f() requires C<T *> {
       //
      }

      void g() {
       f<int &>();
      }

Is the constraint not satisfied because an intermediate invalid type "pointer to reference to `T`" is formed? Or is the constraint satisfied because `T` is never actually used?

#### 2686. Pack expansion into a non-pack parameter of a concept

Section: 13.5.2 [temp.constr.constr] Status: open Submitter: Michał Dominiak Date: 2021-10-15

Consider:

      template<typename T>  concept A = true;
      template<typename... T> concept B = A<T...>;

Is this valid? MSVC accepts; gcc and clang reject. See also issue 1430 for the equivalent question for alias templates.

#### 2996. Impenetrable definition of atomic constraint

Section: 13.5.2.3 [temp.constr.atomic] Status: open Submitter: Brian Bi Date: 2025-01-30

The definition of "atomic constraint" is hard to read.

Possible resolution:

Change in 13.5.2.3 [temp.constr.atomic] paragraph 1 as follows:

> An atomic constraint is formed from an expression E and a mapping from the template parameters that appear within E to template arguments that are formed via substitution during constraint normalization in the declaration of a constrained entity (and, therefore, can involve the unsubstituted template parameters of the constrained entity), called the parameter mapping (13.5.3 [temp.constr.decl]).

> An atomic constraint is a pair (E, M) where E is an expression and M is a mapping from the template parameters that appear within E to template arguments expressed using the normalizing context of the atomic constraint. The parameter mapping of an atomic constraint is M. The normalizing context of an atomic constraint is

> - the surrounding context of the last concept-id for which arguments were substituted into the parameter mapping via constraint normalization (13.5.4 [temp.constr.normal]), if any, and
> - the surrounding context where E appears otherwise.

> [Note: The atomic constraints in the associated constraints of a constrained entity have parameter mappings that map the template parameters that appear within their expressions to template arguments expressed using the context of the declaration of the constrained entity, which may involve the unsubstituted template parameters of the constrained entity. -- end note]

#### 2975. Effect of concept template-head on parameter mappings

Section: 13.5.4 [temp.constr.normal] Status: open Submitter: Hubert Tong Date: 2024-12-24

(From submission #659.)

Consider:

      template <auto> constexpr bool B = true;

      template <unsigned X0> concept C = B<X0>;
      template <unsigned short X1> concept C2 = C<X1>;

      template <unsigned X> void f() requires C<X>; // #1
      template <unsigned X> int f() requires C2<X> && true; // #2

      void g() {
        return f<65536>(); // should probably call #1
      }
      void h() {
        f<0>(); // ambiguous?
      }

The rules in 13.5.4 [temp.constr.normal] bullet 1.4 do not specify how the type of the non-type template parameter `X1` in the definition of concept `C2` affects the parameter mapping. There is implementation divergence: gcc and MSVC reject the call in `g` and accept the call in `h`, resolving to #2 in both cases. Clang and EDG accept the call in `g` (resolving to #1) and reject the call in `h` as ambiguous.

As a suggested resolution, introduce a shadow constraint that checks the validity of the template argument for the concept's template parameter when normalizing the use of `C2` from #2. This would reject the call to #2 from `g` (choosing #1) and select #2 as more constrained for the call from `h`.

#### 3021. Subsumption rules for fold expanded constraints

Section: 13.5.5 [temp.constr.order] Status: open Submitter: Hubert Tong Date: 2025-03-25

Consider:

      template <typename T>
      concept C = false;

      template <typename ...T>
      concept CC = (C<T> && ...);

      template <typename T> requires CC<T>
      struct A;

      template <typename T> requires CC<> && true
      struct A<T>; // okay, surprisingly

The subsumption rules for fold expanded constraints (13.5.5 [temp.constr.order] bullet 1.3 with the definition of "compatible for subsumption" in 13.5.2.5 [temp.constr.fold] paragraph 5) do not properly account for fold-expressions in concept definitions. For the example above, the partial specialization is considered to be more specialized (and its associated constraints can be satisfieid), even though the constraints of the primary template can never be satisifed.

Additionally, replacing fold expressions in a constraint expression with an analogous use of a defined concept breaks subsumption relationships:

      template <typename, unsigned = 0> constexpr bool Atomic = true;

      template <typename T> concept C = Atomic<T>;
      template <typename T> concept C2 = C<T> && Atomic<T, 1>;

      template <typename ...T> concept CC = (C<T> && ...);
      template <typename ...T> concept CC2 = (C2<T> && ...);

      template <typename ...T> requires (C<T> && ...)
      struct A;
      template <typename ...T> requires (C2<T> && ...)
      struct A<T ...>; // okay, compatible for subsumption

      template <typename ...T> requires CC<T ...>
      struct AA;
      template <typename ...T> requires CC2<T ...>
      struct AA<T ...>; // error

Introducing parameter mappings for such fold expanded constraints might be an approach to fixing the issue.

#### 3027. Equivalence of pack-index-specifiers

Section: 13.6 [temp.type] Status: open Submitter: Corentin Jabot Date: 2025-04-14

(From submission #700.)

Subclause 13.6 [temp.type] paragraph 5 and paragraph 6 specify:

> For a type template parameter pack T, T...[constant-expression] denotes a unique dependent type.

> If the constant-expression of a pack-index-specifier is value-dependent, two such pack-index-specifier s refer to the same type only if their constant-expressions are equivalent (13.7.7.2 [temp.over.link]). Otherwise, two such pack-index-specifier s refer to the same type only if their indexes have the same value.

That seems to imply that pack-index-specifiers referring to different template parameter packs could be equivalent.

Possible resolution:

Change and merge in 13.6 [temp.type] paragraph 5 and paragraph 6 as follows:

> For a type template parameter pack `T`, `T`...[constant-expression] denotes a unique dependent type. If the constant-expression of a pack-index-specifier is value-dependent, two Two such pack-index-specifiers (9.2.9.4 [dcl.type.pack.index]) refer to the same type only if their typedef-names refer to the same template parameter pack and their constant-expressions are equivalent if value-dependent (13.7.7.2 [temp.over.link]) or otherwise have the same value. Otherwise, two such pack-index-specifier s refer to the same type only if their indexes have the same value.

#### 2951. Distinguishing a primary template

Section: 13.7.1 [temp.decls.general] Status: open Submitter: Christopher Di Bella Date: 2023-04-22

Consider:

      template<class...>
      struct S {};

      template<>
      struct S<> {};

According to 13.7.1 [temp.decls.general] paragraph 2:

> A primary template declaration is one in which the name of the template is not followed by a template-argument-list. ...

In the example's template-id, the name of the template `S` is, in fact not followed by a template-argument-list (it is absent).

Possible resolution:

Change in 13.7.1 [temp.decls.general] paragraph 2

> A primary template declaration is one in which the name of the template is not followed by a `<` template-argument-listopt `>`. ...

#### 1432. Newly-ambiguous variadic template expansions

Section: 13.7.4 [temp.variadic] Status: open Submitter: Daniel Krügler Date: 2011-12-17

With the new core rules in regard to variadic pack expansions the library specification of the traits template `common_type` is now broken, the reason is that it is defined as a series of specializations of the primary template

        template <class ...T>
        struct common_type;

The broken one is this pair:

      template <class T, class U>
      struct common_type<T, U> {
       typedef decltype(true ? declval<T>() : declval<U>()) type;
      };

      template <class T, class U, class... V>
      struct common_type<T, U, V...> {
       typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
      };

With the new rules both specializations would now be ambiguous for an instantiation like `common_type<X, Y>`.

(See also issue 1395.)

Notes from the October, 2012 meeting:

It is possible that 13.7.6.3 [temp.spec.partial.order] may resolve this problem.

CWG 2022-11-11

Resolved by issue 1395; see the special rule about trailing packs in 13.10.3.5 [temp.deduct.partial] paragraph 11.

Additional notes (March, 2025)

Issue 1395 covers only function parameter packs, but the example in the present issue does not have any. Reopening the issue for further consideration. See also 13.10.3.6 [temp.deduct.type] bullet 9.1.

#### 2118. Stateful metaprogramming via friend injection

Section: 13.7.5 [temp.friend] Status: open Submitter: Richard Smith Date: 2015-04-27

Defining a friend function in a template, then referencing that function later provides a means of capturing and retrieving metaprogramming state. This technique is arcane and should be made ill-formed.

Notes from the May, 2015 meeting:

CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.

#### 708. Partial specialization of member templates of class templates

Section: 13.7.6 [temp.spec.partial] Status: open Submitter: James Widman Date: 8 Aug, 2008

The Standard does not appear to specify clearly the effect of a partial specialization of a member template of a class template. For example:

        template<class T> struct B {
             template<class U> struct A { // #1
                 void h() {}
             };
             template<class U> struct A<U*> {  // #2
                 void f() {}
             };
        };

        template<> template<class U> struct B<int>::A { // #3
             void g() {}
        };

        void q(B<int>::A<char*>& p) {
             p.f();  // #4
        }

The explicit specialization at #3 replaces the primary member template #1 of `B<int>`; however, it is not clear whether the partial specialization #2 should be considered to apply to the explicitly-specialized member template of `A<int>` (thus allowing the call to `p.f()` at #4) or whether the partial specialization will be used only for specializations of `B` that are implicitly instantiated (meaning that #4 could call `p.g()` but not `p.f()`).

#### 2173. Partial specialization with non-deduced contexts

Section: 13.7.6 [temp.spec.partial] Status: open Submitter: Mike Miller Date: 2015-09-14

During the discussion of issue 1315, it was observed that the example

      template <int I, int J> struct B {};
      template <int I> struct B<I, I*2> {};

is ill-formed because the deduction succeeds in both directions. This seems surprising. It was suggested that perhaps a non-deduced context should be considered more specialized than a deduced context.

#### 2948. Late ambiguity for partial template specialization

Section: 13.7.6.1 [temp.spec.partial.general] Status: open Submitter: Mauro Russo Date: 2024-11-01

If a partial specialization is introduced later than where it would have been used, the program is ill-formed, no diagnostic required. The same should apply if the late declaration would have caused an ambiguity.

Also consider removing the redundancy between 13.4.4 [temp.arg.template] paragraph 2 and 13.7.6.1 [temp.spec.partial.general] paragraph 1 for that matter.

Possible resolution:

1. Change in 13.4.4 [temp.arg.template] paragraph 2 as follows:

   > Any partial specializations (13.7.6 [temp.spec.partial]) associated with the primary template are considered when a specialization based on the template template-parameter is instantiated. If a specialization is not reachable from the point of instantiation, and it would have been selected had it been reachable, the program is ill-formed, no diagnostic required. [ Note: A partial specialization cannot be introduced after its first potential use (13.7.6.1 [temp.spec.partial.general]). -- end note ]

2. Change in 13.7.6.1 [temp.spec.partial.general] paragraph 1 as follows:

   > A partial specialization of a template provides an alternative definition of the template that is used instead of the primary definition when the arguments in a specialization match those given in the partial specialization (13.7.6.2 [temp.spec.partial.match]). A declaration of the primary template shall precede any partial specialization of that template. A partial specialization shall be reachable from any use of a template specialization that would make use of the partial specialization as the result of an implicit or explicit point of instantiation where that partial specialization would have been selected or would have contributed to ambiguity; no diagnostic is required.

#### 310. Can function templates differing only in parameter cv-qualifiers be overloaded?

Section: 13.7.7.2 [temp.over.link] Status: open Submitter: Andrei Iltchenko Date: 29 Aug 2001

I get the following error diagnostic [from the EDG front end]:

    line 8: error: function template "example<T>::foo<R,A>(A)" has
              already been declared
         R  foo(const A);
            ^

when compiling this piece of code:

    struct  example  {
       template<class R, class A>   // 1-st member template
       R  foo(A);
       template<class R, class A>   // 2-nd member template
       const R  foo(A&);
       template<class R, class A>   // 3-d  member template
       R  foo(const A);
    };

    /*template<> template<>
    int  example<char>::foo(int&);*/

    int  main()
    {
       int  (example<char>::* pf)(int&) =
          &example<char>::foo;
    }

The implementation complains that

       template<class R, class A>   // 1-st member template
       R  foo(A);
       template<class R, class A>   // 3-d  member template
       R  foo(const A);

cannot be overloaded and I don't see any reason for it since it is function template specializations that are treated like ordinary non-template functions, meaning that the transformation of a parameter-declaration-clause into the corresponding parameter-type-list is applied to specializations (when determining its type) and not to function templates.

What makes me think so is the contents of 13.7.7.2 [temp.over.link] and the following sentence from 13.10.3.2 [temp.deduct.call] "If P is a cv-qualified type, the top level cv-qualifiers of P are ignored for type deduction". If the transformation was to be applied to function templates, then there would be no reason for having that sentence in 13.10.3.2 [temp.deduct.call].

13.10.3.3 [temp.deduct.funcaddr], which my example is based upon, says nothing about ignoring the top level cv-qualifiers of the function parameters of the function template whose address is being taken.

As a result, I expect that template argument deduction will fail for the 2-nd and 3-d member templates and the 1-st one will be used for the instantiation of the specialization.

#### 2584. Equivalent types in function template declarations

Section: 13.7.7.2 [temp.over.link] Status: open Submitter: Jim X Date: 2022-04-08

According to 6.4.1 [basic.scope.scope] paragraph 4:

> 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 function templates with equivalent non-object-parameter-type-lists, return types (if any), template-heads, and trailing requires-clauses (if any), and, if both are non-static members, they have corresponding object parameters.

Assuming that two non-object-parameter-type-lists are equivalent if they have the same length and corresponding types are equivalent, the question remains when two (possibly dependent) types are equivalent. Subclause 13.7.7.2 [temp.over.link] should provide an answer, but only covers expressions appearing in such types (paragraph 5):

> Two expressions involving template parameters are considered equivalent if...

For example, the standard should specify whether these declarations correspond:

      template<class T> T   f();
      template<class T> T&& f();

      template<class T, class U> void g(decltype(T::foo));
      template<class T, class U> void g(decltype(U::foo));

A related issue is the determination whether two names are the same; for example:

      struct A {
        template<class T>
        operator T();

        template<class T>
        operator T&&();
      };

The latter issue could probably be fixed by amending 11.4.8.3 [class.conv.fct] to state that two conversion-function-ids are the same if their conversion-type-ids denote equivalent types, with a cross-reference to 13.7.7.2 [temp.over.link].

#### 2946. Dependent call equivalence in non-ADL cases

Section: 13.7.7.2 [temp.over.link] Status: open Submitter: Hubert Tong Date: 2024-10-30

(From submission #631.)

It is unclear whether the resolution of issue 1321 covers cases where argument-dependent lookup is not used, for example because a callable object or a block-scope extern declartion was found. There is implementation divergence (EDG accepts, others reject) for the following example:

      template <typename ...T>
      struct Blob : T ... {
        using T::operator() ...;
      };
      template <typename T> constexpr bool IsInt = false;
      template <> constexpr bool IsInt<int> = true;
      template <typename T> concept C = IsInt<T>;
      namespace N {
        constexpr auto f() {
          int f(int);
          return [](auto x) requires C<decltype(f(x))> { return true; };
        }
      }
      namespace M {
        constexpr auto f() {
          short f(int);
          return [](auto x) requires C<decltype(f(x))> || (sizeof(x) == 4) {};
        }
      }
      template <typename ...T>
      constexpr Blob<T ...> blobber(T ...) { return {}; }
      static_assert(blobber(N::f(), M::f())(0));

The wording says the example is well-formed, because the use of `C` in `N::f` subsumes the use of `C` in `M::f`, despite the fact that the use of `C` in `M::f` can never be satisfied.

Additionally, a call of the form `(f)(x,y)` (with a parenthesized postfix-expression) is not considered a dependent call per 13.8.3.1 [temp.dep.general] paragraph 2, leaving the issue addressed by issue 1321 open for such cases.

Suggested resolution (incomplete):

1. Change in 13.7.7.2 [temp.over.link] paragraph 5 as follows, adding bullets:

   > ... For determining whether two dependent names (13.8.3 [temp.dep]) are equivalent, the following rules apply:
   > - If name lookup finds a non-function, that entity is considered.
   > - Otherwise, if name lookup finds a set of block-scope declarations (including using-declarations), that set is considered.
   > - Otherwise, if the name appears in a class scope, the lookup set for the name, merged with any dependent using-declarations, is considered.
   > - Otherwise, if the parenthesized name is the postfix-expression of a dependent call, the name itself and the namespace scope is considered, not the result of name lookup.
   > - Otherwise, if the name is the postfix-expression in a dependent call, only the name itself is considered, not the result of name lookup.
   > [Note 5: If such a dependent name is unqualified, it is looked up from a first declaration of the function template (13.8.1 [temp.res.general]). —end note]

2. Change in 13.8.3.1 [temp.dep.general] paragraph 2 as follows:

   > A dependent call is an expression, possibly formed as a non-member candidate for an operator (12.2.2.3 [over.match.oper]), of the form:

   >       postfix-expression ( expression-listopt )

   > where the postfix-expression is an a (possibly parenthesized) unqualified-id and
   > - any of the expressions in the expression-list is a pack expansion (13.7.4 [temp.variadic]), or
   > - any of the expressions or braced-init-lists in the expression-list is type-dependent (13.8.3.3 [temp.dep.expr]), or
   > - the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.

#### 402. More on partial ordering of function templates

Section: 13.7.7.3 [temp.func.order] Status: open Submitter: Nathan Sidwell Date: 7 Apr 2003

This was split off from issue 214 at the April 2003 meeting.

Nathan Sidwell: John Spicer's proposed resolution does not make the following well-formed.

      template <typename T> int Foo (T const *) {return 1;} //#1
      template <unsigned I> int Foo (char const (&)[I]) {return 2;} //#2

      int main ()
      {
        return Foo ("a") != 2;
      }

Both #1 and #2 can deduce the "a" argument, #1 deduces T as char and #2 deduces I as 2. However, neither is more specialized because the proposed rules do not have any array to pointer decay.

#1 is only deduceable because of the rules in 13.10.3.2 [temp.deduct.call] paragraph 2 that decay array and function type arguments when the template parameter is not a reference. Given that such behaviour happens in deduction, I believe there should be equivalent behaviour during partial ordering. #2 should be resolved as more specialized as #1. The following alteration to the proposed resolution of DR214 will do that.

Insert before,

- If A is a cv-qualified type, A is replaced by the cv-unqualified version of A.

the following

- If P was not originally a reference type,
  - If A is an array type, A is replaced by the pointer type produced by the array to pointer conversion
  - If A is a function type, A is replaced by the pointer type produced by the function to pointer conversion

For the example above, this change results in deducing 'T const *' against 'char const *' in one direction (which succeeds), and 'char [I]' against 'T const *' in the other (which fails).

John Spicer: I don't consider this a shortcoming of my proposed wording, as I don't think this is part of the current rules. In other words, the resolution of 214 might make it clearer how this case is handled (i.e., clearer that it is not allowed), but I don't believe it represents a change in the language.

I'm not necessarily opposed to such a change, but I think it should be reviewed by the core group as a related change and not a defect in the proposed resolution to 214.

Notes from the October 2003 meeting:

There was some sentiment that it would be desirable to have this case ordered, but we don't think it's worth spending the time to work on it now. If we look at some larger partial ordering changes at some point, we will consider this again.

#### 1157. Partial ordering of function templates is still underspecified

Section: 13.7.7.3 [temp.func.order] Status: open Submitter: CA Date: 2010-08-03
N3092 comment CA 7

13.7.7.3 [temp.func.order] paragraph 3 says,

> To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (13.7.4 [temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

The characteristics of the synthesized entities and how they are determined is not specified. For example, members of a dependent type referred to in non-deduced contexts are not specified to exist, even though the transformed function type would be invalid in their absence.

Example 1:

      template<typename T, typename U> struct A;
      template<typename T> void foo(A<T, typename T::u> *) { } // #1
        // synthetic T1 has member T1::u
      template <typename T> void foo(A<T, typename T::u::v> *) { } // #2
        // synthetic T2 has member T2::u and member T2::u::v
        // T in #1 deduces to synthetic T2 in partial ordering;
        // deduced A for the parameter is A<T2, T2::u> * --this is not necessarily compatible
        // with A<T2, T2::u::v> * and it does not need to be. See Note 1. The effect is that
        // (in the call below) the compatibility of B::u and B::u::v is respected.
        // T in #2 cannot be successfully deduced in partial ordering from A<T1, T1::u> *;
        // invalid type T1::u::v will be formed when T1 is substituted into non-deduced contexts.
      struct B {
        struct u { typedef u v; };
      };
      int main() {
        foo((A<B, B::u> *)0); // calls #2
      }

Note 1: Template argument deduction is an attempt to match a `P` and a deduced `A`; however, template argument deduction is not specified to fail if the `P` and the deduced `A` are incompatible. This may occur in the presence of non-deduced contexts. Notwithstanding the parenthetical statement in 13.10.3.5 [temp.deduct.partial] paragraph 9, template argument deduction may succeed in determining a template argument for every template parameter while producing a deduced `A` that is not compatible with the corresponding `P`.

Example 2:

      template <typename T, typename U, typename V> struct A;
      template <typename T>
        void foo(A<T, struct T::u, struct T::u::u> *); // #2.1
          // synthetic T1 has member non-union class T1::u
      template <typename T, typename U>
        void foo(A<T, U , U> *); // #2.2
          // synthetic T2 and U2 has no required properties
          // T in #2.1 cannot be deduced in partial ordering from A<T2, U2, U2> *;
          // invalid types T2::u and T2::u::u will be formed when T2 is substituted in nondeduced contexts.
          // T and U in #2.2 deduces to, respectively, T1 and T1::u from A<T1, T1::u, struct
    T1::u::u> * unless
          // struct T1::u::u does not refer to the injected-class-name of the class T1::u (if that is possible).
      struct B {
        struct u { };
      };
      int main() {
        foo((A<B, B::u, struct B::u::u> *)0); // calls #2.1
      }

It is, however, unclear to what extent an implementation will have to go to determine these minimal properties.

Additional notes (March, 2025)

See also issue 2160 .

#### 2160. Issues with partial ordering

Section: 13.7.7.3 [temp.func.order] Status: open Submitter: Richard Smith Date: 2015-07-16

(From this editorial issue.)

Consistency of deduced values

      template <typename T> void foo(T, T); // (1)
      template <typename T, typename U> void foo(T, U); // (2)

13.10.3.6 [temp.deduct.type] paragraph 2 makes it clear that there must be exactly one set of deduced values for the `P`s. But there is no such statement in the partial ordering rule. The algorithm described only does pairwise `P`/`A` matching, so a synthesized call from (2) to (1) via `foo(U{}, V{})` could succeed in deduction. Both gcc and clang agree that (1) is more specialized.

Type Synthesis Template Instantiation

      template <typename T>
      struct identity { using type = T; };

      template<typename T> void bar(T, T ); // (1)
      template<typename T> void bar(T, typename identity<T>::type ); // (2)

Here, if synthesized for (2) `Unique2` and `typename identity<Unique2>::type == Unique2` , then type deduction would succeed in both directions and the call `bar(0,0)` would be ambiguous. However, it seems that both compilers instead simply treat `typename identity<Unique2>::type` as `Unique2_b`, thus making template deduction from (2) to (1) fail (based on the implied missing Consistency rule).

Non-deduced Context Omission

This is the same as the previous example, except now define

      template <typename T> struct identity;
      template <> struct identity<int> { using type = int; };

With no template instantiation during synthesis and consistency, the (2) ==> (1) deduction fails. But if we consider the (1) ==> (2) call, we'd match `T` against `Unique1` and then have the non-deduced context `typename identity<Unique1>::type` to match against `Unique1`, but that would be a substitution failure. It seems that the approach taken by gcc and clang (both of which prefer (1) here) is to ignore the non-deduced context argument, as long as that parameter type is deduced from a different template parameter type that did get matched.

Notes from the February, 2016 meeting:

None of these examples appears to reflect a defect in the current wording; in particular, the second and third examples involve a dependent type and there could be a later specialization of `identity`, so it's impossible to reason about those cases in the template definition context. The issue will be left open to allow for possible clarification of the intent of the wording.

See also issue 1157.

#### 2949. Treatment of ellipsis during partial ordering

Section: 13.7.7.3 [temp.func.order] Status: open Submitter: Artyom Kolpakov Date: 2024-11-02

(From submission #632.)

The partial ordering rules in 13.7.7.3 [temp.func.order] paragraph 6 do not consider the possible presence of an ellipsis in the function parameter list:

- The length of a template-parameter-list is not defined.
- The term "function parameter list" is not defined.
- The length of a function parameter list is not defined: Does an ellipsis count towards the length?
- What does it mean for corresponding function parameter to be of the same type, if one of those parameters is an ellipsis?

Suggested resolution:

Change in 13.7.7.3 [temp.func.order] paragraph 6 and add bullets as follows:

> If deduction against the other template succeeds for both transformed templates, constraints can be considered as follows:
> - If
>   - their template-parameter-lists (possibly including template-parameters invented for an abbreviated function template (9.3.4.6 [dcl.fct])) have different numbers of template-parameters or
>   - function parameter lists differ in length
>   - their parameter-type-lists have different numbers of parameters or
>   - either, but not both, of their parameter-type-lists has an ellipsis,
>   neither template is more specialized than the other.
> - Otherwise: ...

#### 1286. Equivalence of alias templates

Section: 13.7.8 [temp.alias] Status: open Submitter: Gabriel Dos Reis Date: 2011-04-03

Issue 1244 was resolved by changing the example in 13.6 [temp.type] paragraph 1 from

      template<template<class> class TT> struct X { };
      template<class> struct Y { };
      template<class T> using Z = Y<T>;
      X<Y> y;
      X<Z> z;

to

      template<class T> struct X { };
      template<class> struct Y { };
      template<class T> using Z = Y<T>;
      X<Y<int> > y;
      X<Z<int> > z;

In fact, the original intent was that the example should have been correct as written; however, the normative wording to make it so was missing. The current wording of 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 [temp.alias] 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:

>       template<typename T, U = T> struct A;

>       template<typename V, typename W>
>         using B = A<V, W>;                // equivalent to A

>       template<typename V, typename W>
>         using C = A<V>;                   // not equivalent to A:
>                                           // not all parameters used

>       template<typename V>
>         using D = A<V>;                   // not equivalent to A:
>                                           // different number of parameters

>       template<typename V, typename W>
>         using E = A<W, V>;                // not equivalent to A:
>                                           // template-arguments in wrong order

>       template<typename V, typename W = int>
>         using F = A<V, W>;                // equivalent to A:
>                                           // default arguments not considered

>       template<typename V, typename W>
>         using G = A<V, W>;                // equivalent to A and B

>       template<typename V, typename W>
>         using H = E<V, W>;                // equivalent to E

>       template<typename V, typename W>
>         using I = A<V, typename W::type>; // not equivalent to A:
>                                           // argument not identifier

> —end example]

2. Change 13.6 [temp.type] 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`.

>       template<class T template<class> class TT> struct X { };
>       template<class> struct Y { };
>       template<class T> using Z = Y<T>;
>       X<Y<int> Y> y;
>       X<Z<int> Z> z;

> declares `y` and `z` to be of the same type. —end example]

Additional note, November, 2014:

Concern has been expressed over the proposed resolution with regard to its handling of default template arguments that differ between the template and its alias, e.g.,

       template<typename T, typename U = int> struct A {};
       template<typename T, typename U = char> using B = A<T, U>;
       template<template<typename...> typename C> struct X { C<int> c; };

Notes from the May, 2015 meeting:

See also issue 1979, which CWG is suggesting to be resolved by defining a “simple” alias, one in which the SFINAE conditions are the same as the referenced template and that uses all template parameters.

#### 1430. Pack expansion into fixed alias template parameter list

Section: 13.7.8 [temp.alias] Status: open Submitter: Jason Merrill Date: 2011-12-13

Originally, a pack expansion could not expand into a fixed-length template parameter list, but this was changed in N2555. This works fine for most templates, but causes issues with alias templates.

In most cases, an alias template is transparent; when it's used in a template we can just substitute in the dependent template arguments. But this doesn't work if the template-id uses a pack expansion for non-variadic parameters. For example:

        template<class T, class U, class V>
        struct S {};

        template<class T, class V>
        using A = S<T, int, V>;

        template<class... Ts>
        void foo(A<Ts...>);

There is no way to express `A<Ts...>` in terms of `S`, so we need to hold onto the `A` until we have the `Ts` to substitute in, and therefore it needs to be handled in mangling.

Currently, EDG and Clang reject this testcase, complaining about too few template arguments for A. G++ did as well, but I thought that was a bug. However, on the ABI list John Spicer argued that it should be rejected.

(See also issue 1558.)

Notes from the October, 2012 meeting:

The consensus of CWG was that this usage should be prohibited, disallowing use of an alias template when a dependent argument can't simply be substituted directly into the type-id.

Additional note, April, 2013:

For another example, consider:

      template<class... x> class list{};
      template<class a, class... b> using tail=list<b...>;
      template <class...T> void f(tail<T...>);

      int main() {
        f<int,int>({});
      }

There is implementation variance in the handling of this example.

CWG 2022-11-11

There is no more implementation divergence; all known implementations reject the example.

Additional notes (January, 2025)

Using pack-index-specifiers allows to express some such substitutions, but not others. In the below example, we cannot express `X`. And while we can express `Y`, we cannot deduce through the replacement.

      template<typename A, typename...T> using X = std::tuple<T...>;
      template<typename A, typename...T> using Y = A;

      template<typename ...U> void f(X<U...> x, Y<U...> y);

Additional notes (March, 2025)

There is still implementation divergence for the following example:

       template <template <typename...> class T> struct Template {
       template <typename... Us> using type = T<Us...>;
       };

       template <class T> struct X { static constexpr T value = T(); };
       template <class T> using alias = X<T>;

       int main() { return Template<alias>::type<int>::value; }

GCC and EDG accept, clang rejects.

Also consider:

      template <template <int...> class T> struct Template {
        template <int... Us> using type = T<Us...>;
      };
      template <T1 N> struct X { static constexpr int value = 0; };
      template <T2 N> using alias = X<N>;

If T1 = T2 = int, GCC and EDG accept and MSVC and Clang reject. If T1 = int and T2 = short, GCC accepts and everyone else rejects. If T1 = short and T2 = int, EDG accepts and everyone else rejects. See also issues 1979 and 1286; the alias template above would be considered "simple" only if T1 = T2.

#### 2794. Uniqueness of lambdas in alias templates

Section: 13.7.8 [temp.alias] Status: open Submitter: Ilya Biryukov Date: 2023-04-19

Consider:

      template<class T> using A = decltype([]{});

      static_assert(std::same_as<A<int>, A<int>>);

There is implementation divergence: GCC and MSVC accept, clang rejects. A similar question arises if token-identical alias templates appear in different translation units:

      // appearing in multiple translation units
      template<typename T> using A = decltype([]{});
      inline A<int> f() { return {}; }

An alias template is a templated entity per 13.1 [temp.pre] paragraph 8.1, thus it is a definable item (6.3 [basic.def.odr] paragraph 1.5) and thus there is effectively only a single definition of it (6.3 [basic.def.odr] paragraph 15).

However, that reasoning does not address the question whether `A<int>` denotes the same type when it appears repeatedly in the same translation unit. Consider 13.7.7.2 [temp.over.link] paragraph 5:

> Two lambda-expressions are never considered equivalent.

and 13.7.8 [temp.alias] paragraph 2

> When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameter s in the defining-type-id of the alias template.

This means that a template-id referring to an alias template performs a fresh substitution each time it appears. There is no concept of instantiating an alias template to produce an alias declaration. Subclause 13.7.8 [temp.alias] paragraph 5 specifies:

> The type of a lambda-expression appearing in an alias template declaration is different between instantiations of that template, even when the lambda-expression is not dependent.

The outcome seems unfortunate; the first example ought to be well-formed.

Additional notes (January, 2025)

Another example:

      template<typename T, typename F = decltype([](){} )>
      struct Type {};
      template<typename A>
      using C = Type<A>;

      static_assert(std::same_as<Type<int>,Type<int>>); // #1
      static_assert(std::same_as<C<int>,C<int>>); // #2

All of clang, EDG, gcc, and MSVC reject #1; clang additionally rejects #2.

There is also implementation divergence for the following example:

      template<typename T> using A = decltype([]{});
      template<typename T> void f(A<T>) {}
      template<typename T> void f(A<T>) {}  // Duplicate definition?

MSVC and gcc reject as a redefinition; clang and EDG accept. Even if only a single definition is present, implementations differ about whether `f` has internal or external linkage and what properties the mangled name has.

Issue 1979 is related and suggests to treat simple alias templates differently (those would not have specializations, but be transparent for template argument deduction) from more complicated ones (those would have specializations and be morally equivalent to a `struct` with a member typedef).

#### 1257. Instantiation via non-dependent references in uninstantiated templates

Section: 13.8 [temp.res] Status: open Submitter: Johannes Schaub Date: 2011-03-09

The Standard does not appear to specify whether a non-dependent reference to a template specialization in a template definition that is never instantiated causes the implicit instantiation of the referenced specialization.

#### 2067. Generated variadic templates requiring empty pack

Section: 13.8 [temp.res] Status: open Submitter: Richard Smith Date: 2015-01-09

According to 13.8 [temp.res] paragraph 8,

> If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

I'm inclined to think that this rule should only apply to code the user wrote. That is, if every valid instantiation of an entity (that was not itself instantiated) requires at least one of the enclosing template argument lists to include an empty template argument pack, then the program is ill-formed (no diagnostic required).

#### 2462. Problems with the omission of the `typename` keyword

Section: 13.8.1 [temp.res.general] Status: open Submitter: Mark Hall Date: 2020-12-03

According to 13.8.2 [temp.local] 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

       template<typename T> struct S {
         static void (*pfunc)(T::name);                               // Omitted typename okay because it is a
                                                                      // member-declaration
       };
       template<typename T> void (*S<T>::pfunc)(T::name) = nullptr;   // Omitted typename ill-formed because not a function
                                                                      // or function template declaration

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

       template<typename T> struct Y {};
       template<typename T> struct S {
         Y<int(T::type)> m;  // Omitted typename okay because it is in a member-declaration?
      };

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 issue 2468 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: open Submitter: Mark Hall Date: 2020-12-03

According to 13.8.2 [temp.local] 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:

       template<typename T> struct Y {};
       template<typename T> struct S {
         Y<int(T::type)> m;  // Omitted typename okay because it is in a member-declaration?
      };

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 issue 2462 to allow the resolutions to proceed independently.)

#### 186. Name hiding and template template-parameters

Section: 13.8.2 [temp.local] Status: open Submitter: John Spicer Date: 11 Nov 1999

The standard prohibits a class template from having the same name as one of its template parameters (13.8.2 [temp.local] paragraph 4) . This prohibits

        template <class X> class X;

for the reason that the template name would hide the parameter, and such hiding is in general prohibited.

Presumably, we should also prohibit

        template <template <class T> class T> struct A;

for the same reason.

#### 1619. Definition of current instantiation

Section: 13.8.3.2 [temp.dep.type] Status: open Submitter: Johannes Schaub Date: 2013-02-04

The definition of the current instantiation, given in 13.8.3.2 [temp.dep.type] paragraph 1, is phrased in terms of the meaning of a name (“A name refers to the current instantiation if it is...”); it does not define when a type is the current instantiation. Thus the interpretation of `*this` and of phrases like “member of a class that is the current instantiation” is not formally specified.

#### 2774. Value-dependence of requires-expressions

Section: 13.8.3.4 [temp.dep.constexpr] Status: open Submitter: Christof Meerwald Date: 2023-07-17

Consider:

       template<typename T>
       struct A
       {
         static_assert(sizeof(T) == 3);    // #1
       };

       template<bool B>
       struct C
       {
         static_assert(B);                 // #2
       };

       template<typename T, typename U>
       int f(typename C<requires { typename T::type; }>::type, typename A<T>::type, U);

       int i = f<char>(0, 1, 2);

Is the requires-expression value-dependent after substituting the explicit template argument, thus failing at #2 instead of at #1?

Suggested resolution:

1. Change in 13.8.3.3 [temp.dep.expr] paragraph 4 as follows:

   > Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):

   >       ...
   >       noexcept ( expression )
   >       requires-expression

2. Insert a new paragraph before 13.8.3.4 [temp.dep.constexpr] paragraph 5:

   > A requires-expression is value-dependent if it depends on a template parameter.

   > An expression of the form &qualified-id where the qualified-id names a dependent member ...

CWG 2023-08-25

The first change has consensus and is progressed via issue 2785; the second change should use an approach of recursive decomposition for requires-expression, similar to other paragraphs in 13.8.3.4 [temp.dep.constexpr]. The implementation divergence is believed to be an independent bug in one implementation.

#### 2984. Value-dependent structured bindings

Section: 13.8.3.4 [temp.dep.constexpr] Status: open Submitter: Michael Park Date: 2025-01-14

Consider:

      struct S { int x; };

      template <S s>
      void f() {
       constexpr auto [x] = s;
       constexpr bool b = (x == 0); // x ought to be value-dependent
      }

The rules in 13.8.3.4 [temp.dep.constexpr] do not seem to cover the case of structured bindings.

Possible resolution (January, 2025) [SUPERSEDED]:

Change in 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:

> An id-expression is value-dependent if
> - it is a concept-id and any of its arguments are dependent,
> - it is type-dependent,
> - it is the name of a non-type template parameter,
> - it names a static data member that is a dependent member of the current instantiation and is not initialized in a member-declarator,
> - it names a static member function that is a dependent member of the current instantiation, or
> - it names a potentially-constant variable (7.7 [expr.const]) that is initialized with an expression that is value-dependent. , or
> - it names a structured binding whose associated variable (9.7 [dcl.struct.bind]) is value-dependent.

Possible resolution (February, 2025):

1. Change in 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:

   > An id-expression is value-dependent if
   > - it is a concept-id and any of its arguments are dependent,
   > - it is type-dependent,
   > - it is the name of a non-type template parameter,
   > - it names a static data member that is a dependent member of the current instantiation and is not initialized in a member-declarator,
   > - it names a static member function that is a dependent member of the current instantiation, or
   > - it names a potentially-constant variable (7.7 [expr.const]) that is initialized with an expression that is value-dependent. , or
   > - it names a structured binding whose structured binding declaration (9.7 [dcl.struct.bind]) has a brace-or-equal-initializer that is value-dependent.

2. Add after 13.8.3.4 [temp.dep.constexpr] paragraph 6 as follows:

   > ... An expression of the form &cast-expression is also value-dependent if evaluating cast-expression as a core constant expression (7.7 [expr.const]) succeeds and the result of the evaluation refers to a templated entity that is an object with static or thread storage duration or a member function.

   > A braced-init-list is value-dependent if any element is value-dependent or is a pack expansion.

#### 2090. Dependency via non-dependent base class

Section: 13.8.3.7 [temp.dep.temp] Status: open Submitter: Maxim Kartashev Date: 2015-02-27

According to 13.8.3.7 [temp.dep.temp] paragraph 3,

> a non-type template-argument is dependent if the corresponding non-type template-parameter is of reference or pointer type and the template-argument designates or points to a member of the current instantiation or a member of a dependent type.

Members of non-dependent base classes are members of the current instantiation, but using one as a non-type template argument should not be considered dependent.

#### 3002. Template parameter/argument confusion

Section: 13.8.3.7 [temp.dep.temp] Status: open Submitter: Hubert Tong Date: 2025-02-02

(From submission #668.)

Subclause 13.8.3.7 [temp.dep.temp] paragraph 4 confuses template parameters and arguments.

Suggested resolution:

Change in 13.8.3.7 [temp.dep.temp] paragraph 4 as follows:

> A template template parameter argument is dependent if it names a template parameter or its terminal name is dependent.

#### 2250. Implicit instantiation, destruction, and TUs

Section: 13.8.4.1 [temp.point] Status: open Submitter: Dawn Perchik Date: 2016-03-21

Consider:

      template<class T> struct A {
        T* p;
        A();
        ~A() {
    	if (0 < sizeof (T))
    	  delete p;
        }
      };
      struct U;
      struct C {
        C();
        ~C();
        A<U> u;
      };
      int main() {
        C c;
        return 0;
      }

The intent is that `A<U>::~A` is instantiated where the special member functions of `C` are defined (at which point `U` is ostensibly complete). Preventing instantiation (and thus avoiding the completeness check) in the present translation unit is necessary for the `unique_ptr`-based pimpl idiom to work.

Notes from the December, 2016 teleconference:

The problem is that the current wording only connects name lookup with point of instantiation; other semantic checks, such as the requirement for completeness of a class, should also be performed at that point.

#### 2435. Alias template specializations

Section: 13.9 [temp.spec] Status: open Submitter: Krystian Stasiowski Date: 2019-09-28

According to 13.9 [temp.spec] paragraph 4,

> An instantiated template specialization can be either implicitly instantiated (13.9.2 [temp.inst]) for a given argument list or be explicitly instantiated (13.9.3 [temp.explicit]). A specialization is a class, variable, function, or class member that is either instantiated or explicitly specialized (13.9.4 [temp.expl.spec]).

The definition of “specialization” does not cover alias templates, although the terms “specialization of an alias template” and ”alias template specialization” are used in 13.7.8 [temp.alias]. (Note that there are differences between alias specializations and the specializations mentioned here; in particular, an alias template cannot be explicitly specialized, and it is not the result of instantiation (paragraph 1) but simply of substitution (13.7.8 [temp.alias] paragraph 2).)

#### 1856. Indirect nested classes of class templates

Section: 13.9.2 [temp.inst] Status: open Submitter: Richard Smith Date: 2014-02-11

During the discussion of issue 1484, it was observed that the current rules do not adequately address indirect nested classes of class templates (i.e., member classes of member classes of class templates) in regard to their potential separate instantiation.

#### 3034. Infinite recursion should hit an implementation limit

Section: 13.9.2 [temp.inst] Status: open Submitter: Timur Doumler Date: 2025-07-04

Infinite recursion during template instantiation is specified to yield undefined behavior in 13.9.2 [temp.inst] paragraph 16. However, instantiation should be subject to compile-time implementation limits and not cause runtime undefined behavior.

Suggested resolution:

Change in 13.9.2 [temp.inst] paragraph 16 as follows:

> There is an implementation-defined quantity that specifies the limit on the total depth of recursive instantiations (Clause Annex B [implimits]), which could involve more than one template. The result of an infinite recursion in instantiation is undefined.

#### 293. Syntax of explicit instantiation/specialization too permissive

Section: 13.9.3 [temp.explicit] Status: open Submitter: Mark Mitchell Date: 27 Jun 2001

13.9.3 [temp.explicit] defines an explicit instantiation as

explicit-instantiation:

`template` declaration

Syntactically, that allows things like:

        template int S<int>::i = 5, S<int>::j = 7;

which isn't what anyone actually expects. As far as I can tell, nothing in the standard explicitly forbids this, as written. Syntactically, this also allows:

        template namespace N { void f(); }

although perhaps the surrounding context is enough to suggest that this is invalid.

Suggested resolution:

I think we should say:

explicit-instantiation:

type-specifier-seqopt declaratoropt `;`

[Steve Adamczyk: presumably, this should have `template` at the beginning.]

and then say that:

- The declarator can be omitted only when the type-specifier-seq consists solely of an elaborated-type-specifier for a class type, in which case the instantiation is for the named class. If the declarator is present the instantiation is for the named entity.
- The type-specifier-seq can be omitted only when the declarator is for a constructor, destructor, or conversion operator.
- The type-specifier-seq cannot define any new types.

There are similar problems in 13.9.4 [temp.expl.spec]:

explicit-specialization:

`template` <> declaration

Here, I think we want:

explicit-specialization:

decl-specifier-seqopt init-declaratoropt `;`

with similar restrictions as above.

[Steve Adamczyk: This also needs to have `template <>` at the beginning, possibly repeated.]

#### 1046. What is a “use” of a class specialization?

Section: 13.9.3 [temp.explicit] Status: open Submitter: Michael Wong Date: 2010-03-08

According to 13.9.3 [temp.explicit] paragraph 10,

> An entity that is the subject of an explicit instantiation declaration and that is also used in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.

The term “used” is too vague and needs to be defined. In particular, “use” of a class template specialization as an incomplete type — to form a pointer, for instance — should not require the presence of an explicit instantiation definition elsewhere in the program.

#### 3006. Vague restrictions for explicit instantiations of class templates

Section: 13.9.3 [temp.explicit] Status: open Submitter: Richard Smith Date: 2021-02-11

Consider:

      template<typename T> struct X { };

      extern template struct X<int>;       // OK
      extern template struct X<int>::X;    // well-formed explicit instantiation of X<int> ?

At issue is the phrasing "shall include" in 13.9.3 [temp.explicit] paragraph 4:

> If the explicit instantiation is for a class or member class, the elaborated-type-specifier in the declaration shall include a simple-template-id; otherwise, the declaration shall be a simple-declaration whose init-declarator-list comprises a single init-declarator that does not have an initializer. ...

Furthermore, explicit instantiations with a nested-name-specifier do not seem to be declarative.

Possible resolution:

1. Change in 13.9.3 [temp.explicit] paragraph 4 as follows:

   > If the declaration in an explicit instantiation is an elaborated-type-specifier, it shall be of the form

   >     class-key nested-name-specifieropt templateopt simple-template-id

   > and the explicit instantiation is for a class template or member class, the elaborated-type-specifier in the declaration shall include a simple-template-id; otherwise, the declaration shall be a simple-declaration whose init-declarator-list comprises a single init-declarator that does not have an initializer. ...

2. Change in 7.5.5.3 [expr.prim.id.qual] paragraph 2 as follows:

   > A nested-name-specifier is declarative if it is part of
   > - a class-head-name (11.1 [class.pre]),
   > - an enum-head-name (9.8.1 [dcl.enum]),
   > - a qualified-id that is the id-expression of a declarator-id (9.3.1 [dcl.decl.general]), or
   > - an elaborated-type-specifier of an explicit instantiation (13.9.3 [temp.explicit]), or
   > - a declarative nested-name-specifier.

#### 529. Use of `template<>` with “explicitly-specialized” class templates

Section: 13.9.4 [temp.expl.spec] Status: open 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 [temp.expl.spec] 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:...

2. Change 13.9.4 [temp.expl.spec] 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 [temp.expl.spec] 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:...

2. Change 13.9.4 [temp.expl.spec] 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:...

#### 1993. Use of `template<>` defining member of explicit specialization

Section: 13.9.4 [temp.expl.spec] Status: open Submitter: Richard Smith Date: 2014-08-31

Issue 531 surveyed existing practice at the time and determined that the most common syntax for defining a member of an explicit specialization used the `template<>` prefix. This approach, however, does not seem consistent, since such a definition is not itself an explicit specialization.

#### 264. Unusable template constructors and conversion functions

Section: 13.10.2 [temp.arg.explicit] Status: open Submitter: John Spicer Date: 17 Nov 2000

The note in paragraph 5 of 13.10.2 [temp.arg.explicit] makes clear that explicit template arguments cannot be supplied in invocations of constructors and conversion functions because they are called without using a name. However, there is nothing in the current wording of the Standard that makes declaring a constructor or conversion operator that is unusable because of nondeduced parameters (i.e., that would need to be specified explicitly) ill-formed. It would be a service to the programmer to diagnose this useless construct as early as possible.

#### 697. Deduction rules apply to more than functions

Section: 13.10.3 [temp.deduct] Status: open Submitter: Doug Gregor Date: 6 June, 2008

13.10.3 [temp.deduct] is all about function types, but these rules also apply, e.g., when matching a class template partial specialization. We should add a note stating that we could be doing substitution into the template-id for a class template partial specialization.

Additional note (August 2008):

According to 13.7.6.2 [temp.spec.partial.match] paragraph 2, argument deduction is used to determine whether a given partial specialization matches a given argument list. However, there is nothing in 13.7.6.2 [temp.spec.partial.match] nor in 13.10.3 [temp.deduct] and its subsections that describes exactly how argument deduction is to be performed in this case. It would seem that more than just a note is required to clarify this processing.

#### 1844. Defining “immediate context”

Section: 13.10.3 [temp.deduct] Status: open Submitter: Richard Smith Date: 2014-01-28 Liaison: EWG

The handling of an example like

      template<typename T, std::size_t S = sizeof(T)> struct X {};
      template<typename T> X<T> foo(T*);
      void foo(...);

      void test() { struct S *s; foo(s); }

varies among implementations, presumably because the meaning of “immediate context” in determining whether an error is a substitution failure or a hard error is not clearly defined.

Notes from the February, 2016 meeting:

See also issue 1554; the resolution of this issue should also deal with alias templates.

CWG 2024-06-28

See also issue 2296.

#### 2296. Are default argument instantiation failures in the “immediate context”?

Section: 13.10.3 [temp.deduct] Status: open Submitter: Jason Merrill Date: 2016-06-25 Liaison: EWG

Consider the following example (taken from issue 3 of paper P0348R0):

      template <typename U> void fun(U u = U());

      struct X {
        X(int) {}
      };

      template <class T> decltype(fun<T>()) g(int) { }

      template<> void g(long) { }

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

When is the substitution into the return type done? The current specification makes this example ill-formed because the failure to instantiate the default argument in the `decltype` operand is not in the immediate context of the substitution, although a plausible argument for making this a SFINAE case can be made.

Notes from the June, 2016 meeting:

CWG decided to refer this question to EWG for their consideration.

EWG 2022-11-11

This is tracked in github issue cplusplus/papers#1377. See also paper P2285 (Are default function arguments in the immediate context?), tracked by github issue cplusplus/papers#976.

CWG 2024-06-28

See also issues 1554 and 1844 for other cases of missing clarity for "immediate context".

#### 2498. Partial specialization failure and the immediate context

Section: 13.10.3.1 [temp.deduct.general] Status: open Submitter: Daveed Vandevoorde Date: 2021-06-15

Consider the following example:

      template<typename T, typename U> struct S {};
      template<typename T> struct S<T, T> {};
      template<typename T, typename U> struct S<T*, U*> {};
      template<typename... Ts> using V = void;
      template<typename T, typename U = void> struct X {};
      template<typename T> struct X<T, V<typename S<T, T>::type>>;
      X<int*> xpi;

Determining whether the partial specialization of `X` matches `X<int*>` requires determining whether one of the partial specializations of `S` matches `S<int*,int*>`. The partial specializations of `S` are ambiguous for this case. The question is whether that ambiguity should be considered in the “immediate context” of the type (SFINAE) or whether it should result in a hard error. There is implementation divergence on the handling of this example.

Notes from the November, 2021 teleconference:

A similar example can be constructed involving overload resolution instead of partial specialization:

      template<typename T, typename U> struct S {};
      template<typename T> struct S<T, T> {};
      template<typename T, typename U> struct S<T*, U*> {};

      template<class T>
      bool f(T, typename S<T, T>::type = 0);
      bool f(...);

      int x;
      bool b = f(&x);  // hard error with gcc, ok with clang

#### 2769. Substitution into template parameters and default template arguments should be interleaved

Section: 13.10.3.1 [temp.deduct.general] Status: open Submitter: Richard Smith Date: 2023-07-14

Subclause 13.10.3.1 [temp.deduct.general] paragraph 5 specifies:

> 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. ... When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template are replaced with the corresponding deduced or default argument values.

This description is confused. We need to have already substituted into the template parameter declaration in order to finish forming a template argument, and we need to finish forming a template argument before we can substitute it into a later default template argument. Consider:

      struct X { constexpr operator int() { return 0; } };
      template<const int*> struct Y {};
      extern int arr[];
      template<typename T, T K = X(), const int *p = &arr[K], Y<p> y = {}> struct A {};
      A<int> a;

Here, we need to substitute `T = int` into the type of `K`, then convert the default template argument `X()` to `int`, then substitute the converted value of `p` into the type of `y`. The substitution into template parameters and into default template arguments is necessarily interleaved.

Suggested resolution:

Change in 13.10.3.1 [temp.deduct.general] paragraph 5 as follows:

> The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. For each template parameter in turn:
> - If a template argument has not been deduced and the template parameter is a parameter pack, the template argument is an empty pack.
> - Otherwise, If 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 template parameter does not have a default template argument, or if the substitution results in an invalid type, as described above, type deduction fails. [ Example: ... ]
> - When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template are replaced with the corresponding deduced or default argument values. The substituted template parameter is determined by substituting the template arguments determined for preceding template parameters into the template parameter. If the substitution results in an invalid type, as described above, type deduction fails.
> - The template argument is matched against the substituted template parameter (13.4.1 [temp.arg.general]). If the template argument does not match the substituted template parameter, type deduction fails. [ Note: Matching a template argument to a non-type template parameter may perform a conversion. The converted value is used for later substitutions. -- end note ]
> If the function template has associated constraints (13.5.3 [temp.constr.decl]), those constraints are checked for satisfaction (13.5.2 [temp.constr.constr]). ...

#### 2770. Trailing requires-clause can refer to function parameters before they are substituted into

Section: 13.10.3.1 [temp.deduct.general] Status: open Submitter: Richard Smith Date: 2023-07-14

After the application of issue 2369, satisfaction for a trailing requires-clause is checked prior to substitution into the function type. Subclause 13.10.3.1 [temp.deduct.general] paragraph 5 specifies:

> If the function template has associated constraints (13.5.3 [temp.constr.decl]), those constraints are checked for satisfaction (13.5.2 [temp.constr.constr]). ... If type deduction has not yet failed, then all uses of template parameters in the function type are replaced with the corresponding deduced or default argument values.

However, the associated constraints can refer to parameters, thus substitution into the parameter types must have been already done at the time of checking. Consider:

      template<typename T> void f(T t) requires (sizeof(t) == 5) {}

Suggested resolution:

Function parameters should be instantiated when they are needed by a satisfaction check of an atomic constraint or (recursively) by another function parameter.

#### 503. Cv-qualified function types in template argument deduction

Section: 13.10.3.2 [temp.deduct.call] Status: open Submitter: Gabriel Dos Reis Date: 22 Feb 2005

Consider the following program:

        template <typename T> int ref (T&)                { return 0; }
        template <typename T> int ref (const T&)          { return 1; }
        template <typename T> int ref (const volatile T&) { return 2; }
        template <typename T> int ref (volatile T&)       { return 4; }

        template <typename T> int ptr (T*)                { return 0; }
        template <typename T> int ptr (const T*)          { return 8; }
        template <typename T> int ptr (const volatile T*) { return 16; }
        template <typename T> int ptr (volatile T*)       { return 32; }

        void foo() {}

        int main()
        {
            return ref(foo) + ptr(&foo);
        }

The Standard appears to specify that the value returned from `main` is 2. The reason for this result is that references and pointers are handled differently in template argument deduction.

For the reference case, 13.10.3.2 [temp.deduct.call] paragraph 3 says that “If `P` is a reference type, the type referred to by `P` is used for type deduction.” Because of issue 295, all four of the types for the `ref` function parameters are the same, with no cv-qualification; overload resolution does not find a best match among the parameters and thus the most-specialized function is selected.

For the pointer type, argument deduction does not get as far as forming a cv-qualified function type; instead, argument deduction fails in the cv-qualified cases because of the cv-qualification mismatch, and only the cv-unqualified version of `ptr` survives as a viable function.

I think the choice of ignoring cv-qualifiers in the reference case but not the pointer case is very troublesome. The reason is that when one considers function objects as function parameters, it introduces a semantic difference whether the function parameter is declared a reference or a pointer. In all other contexts, it does not matter: a function name decays to a pointer and the resulting semantics are the same.

(See also issue 1584.)

#### 1939. Argument conversions to nondeduced parameter types revisited

Section: 13.10.3.2 [temp.deduct.call] Status: open Submitter: Richard Smith Date: 2014-06-11

The intent of the resolution of issue 1184 appears not to have been completely realized. In particular, the phrase, “contains no template-parameters that participate in template argument deduction” in both the note in 13.10.3.2 [temp.deduct.call] paragraph 4 and the normative wording in 13.10.2 [temp.arg.explicit] paragraph 6 is potentially misleading and probably should say something like, “contains no template-parameters outside non-deduced contexts.” Also, the normative wording should be moved to 13.10.3.2 [temp.deduct.call] paragraph 4, since it applies when there are no explicitly-specified template arguments. For example,

      template<typename T>
      void f(T, typename identity<T>::type*);

Presumably the second parameter should allow pointer conversions, even though it does contain a template-parameter that participates in deduction (via the first function parameter).

Additional note, October, 2015:

See also issue 1391.

#### 2978. Deduction involving reference to similar types

Section: 13.10.3.2 [temp.deduct.call] Status: open Submitter: Brian Bi Date: 2024-12-11

(From submission #651.)

Consider:

      #include <cstddef>

      template <std::size_t N>
      void f(const int* const (&)[N]);

      int main() {
        int* a[3];
        f(a);
      }

There is implementation divergence.

Possible resolution:

Change in 13.10.3.2 [temp.deduct.call] bullet 4.1 as follows:

> - If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than a type similar to the transformed A that is reference-compatible with the transformed A.
> - ...

#### 1221. Partial ordering and reference collapsing

Section: 13.10.3.5 [temp.deduct.partial] Status: open Submitter: Michael Wong Date: 2010-11-08

The current partial ordering rules produce surprising results in the presence of reference collapsing.

Since partial ordering is currently based solely on the signature of the function templates, the lack of difference following substitution of the template type parameter in the following is not taken into account.

Especially unsettling is that the allegedly "more specialized" template (#2) is not a candidate in the first call where template argument deduction fails for it despite a lack of non-deduced contexts.

        template <typename T>
        void foo(T&&);  // #1

        template <typename T>
        void foo(volatile T&&);  // #2

        int main(void) {
          const int x = 0;
          foo(x);  // calls #1 with T='const int &'
          foo<const int &>(x);  // calls #2
        }

#### 2998. Missing deduction consistency check for partial ordering

Section: 13.10.3.5 [temp.deduct.partial] Status: open Submitter: Richard Smith Date: 2025-03-02

Consider issue 2160, first part:

       template <typename T> void foo(T, T); // (1)
       template <typename T, typename U> void foo(T, U); // (2)

Partial ordering is describing as pairwise (but otherwise independent) P/A matching, missing a check that the deduced values for the various Ps must be consistent per 13.10.3.6 [temp.deduct.type] paragraph 2.

Possible resolution:

Change in 13.10.3.5 [temp.deduct.partial] paragraph 8 as follows:

> Using the set of pairs formed from the corresponding resulting types P and A, the deduction is then done as described in 13.10.3.6 [temp.deduct.type]. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

#### 1763. Length mismatch in template type deduction

Section: 13.10.3.6 [temp.deduct.type] Status: open Submitter: Canada Date: 2013-09-23
N3690 comment CA 4

It is not clear how an example like the following is to be handled:

      template <typename U>
      struct A {
        template <typename V> operator A<V>();
      };

      template <typename T>
      void foo(A<void (T)>);

      void foo();

      int main() {
        A<void (int, char)> a;
        foo<int>(a);
        foo(a); // deduces T to be int
      }

In sub13.10.3.6 [temp.deduct.type] paragraph 10, deduction from a function type considers P/A pairs from the parameter-type-list only where the "P" function type has a parameter. Deduction is not specified to fail if there are additional parameters in the corresponding "A" function type.

Notes from the September, 2013 meeting:

CWG agreed that this example should not be accepted. The existing rules seem to cover this case (deduction is not specified to “succeed,” so it's a reasonable conclusion that it fails), but it might be helpful to be clearer.

#### 2900. Deduction of non-type template arguments with placeholder types

Section: 13.10.3.6 [temp.deduct.type] Status: open Submitter: Hubert Tong Date: 2024-06-05

(From submission #546.)

The deduction rule for non-type template parameters in 13.10.3.6 [temp.deduct.type] paragraph 20seems lacking:

> If P has a form that contains <i>, and if the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails. [ Footnote: ... ] ...

This wording does not address the situation when the declared type of `i` is a placeholder type, or when the type of the corresponding template parameter of the template named by the enclosing simple-template-id is a placeholder type.

Suggested resolution:

Change in 13.10.3.6 [temp.deduct.type] paragraph 20 as follows:

> If P has a form that contains <i>, and if the type of i differs from the type deduction fails unless the type of `i` is the same as that of the corresponding template parameter `p` in the specialization (from A) of the template named by the enclosing simple-template-id, deduction fails ; if the declared type of `i` contains a placeholder type, the corresponding template argument for the purposes of placeholder type deduction (9.2.9.7.2 [dcl.type.auto.deduct]) is an id-expression for `p`. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails. [ Footnote: ... ] ... [ Example 13:

>       template<int i> class A { /* ... */ };
>       template<short s> void f(A<s>);
>       void k1() {
>         A<1> a;
>         f(a);      // error: deduction fails for conversion from int to short
>         f<1>(a);   // OK
>       }
>       template<const short cs> class B { };
>       template<short s> void g(B<s>);
>       void k2() {
>         B<1> b;
>         g(b);  // OK, cv-qualifiers are ignored on template parameter types
>       }

>       template<auto> struct C;
>       template<long long x> void f(C<x> *);
>       void g(C<0LL> *ap) {
>         f(ap);     // OK, deduces long long value from 0LL
>       }

>       template<int> struct D;
>       template<auto x> void f(D<x> *);
>       void g(D<0LL> *ap) {
>         f(ap);   // OK, deduces x as an int value
>       }

>       template<int &> struct E;
>       template<auto x> void f(E<x> *);
>       int v;
>       void g(E<v> *bp) {
>         f(bp);    // error: type int of x does not match the int & type of the template parameter in the E<v> specialization of E
>       }

>       template<const int &> struct F;
>       template<decltype(auto) x> void f(F<x> *);
>       int i;
>       void g(F<i> *ap) {
>         f(ap);   // OK, deduces x as a non-type template parameter of type const int &
>       }

>       template <decltype(auto)> struct G;
>       template <auto x> long *f(G<x> *);   // #1
>       template <decltype(auto) x> short *f(G<x> *); // #2

>       const int j = 0;
>       short *g(G<(j)> *ap) {
>         return f(ap);     // OK, only #2 matches
>       }

>       long *g(G<j> *ap) {
>         return f(ap);     // OK, #1 is more specialized
>       }

> -- end example]

#### 2974. Non-deduced context for qualified-id naming a template

Section: 13.10.3.6 [temp.deduct.type] Status: open Submitter: Hubert Tong Date: 2024-12-27

(From submission #660.)

Subclause 13.10.3.6 [temp.deduct.type] bullet 5.1 specifies as a non-deduced context:

> - The nested-name-specifier of a type that was specified using a qualified-id.
> - ...

This does not cover templates named using a qualified-id:

      template <template <typename> class>
      struct B;

      struct C {
        template <typename>
        struct Nested;
      };

      template <typename T>
      void f(T *, B<T::template Nested> *);

      void g(C *cp) {
        f(cp, 0);           // should be OK
      }

Suggested resolution:

Change in 13.10.3.6 [temp.deduct.type] bullet 5.1 as follows:

> - The nested-name-specifier of a type or template that was specified using a qualified-id.
> - ...

#### 2417. Explicit instantiation and exception specifications

Section: 14.5 [except.spec] Status: open Submitter: John Spicer Date: 2019-06-19

Consider the following example:

      template<class T>struct Y {
        typedef typename T::value_type blah;  // #1
        void swap(Y<T> &);
      };
      template<class T>
      void swap(Y<T>& Left, Y<T>& Right) noexcept(noexcept(Left.swap(Right))) { }

      template <class T> struct Z {
        void swap(Z<T> &);
      };
      template<class T>
      void swap(Z<T>& Left, Z<T>& Right) noexcept(noexcept(Left.swap(Right))) { }

      Z<int> x00, y00;
      constexpr bool b00 = noexcept(x00.swap(y00));
      template void swap<int>(Z<int>&, Z<int>&) noexcept(b00);  // #2

The question here is whether the explicit instantiation of

      swap<int>(Z<int>&, Z<int>&)

at #2 instantiates the exception specification of

      swap<int>(Y<int>&, Y<int>&)

which would instantiate `Y<int>`, resulting in an error on the declaration of

      typedef typename T::value_type blah;

at #1.

According to 13.9.2 [temp.inst] paragraph 14,

> The noexcept-specifier of a function template specialization is not instantiated along with the function declaration; it is instantiated when needed (14.5 [except.spec]).

According to 14.5 [except.spec] bullet 13.3, one of the reasons an exception specification is needed is:

> the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function);

Such a comparison is presumably needed when determining which function template the explicit instantiation is referring to, making the program ill-formed. However, there is implementation variance on this point.

CWG 2022-11-10

There are related problems in this area; CWG is seeking input to form a holistic view.

#### 2002. White space within preprocessing directives

Section: 15.1 [cpp.pre] Status: open Submitter: Richard Smith Date: 2014-09-10 Liaison: WG14

According to 15.1 [cpp.pre] paragraph 5,

> The only white-space characters that shall appear between preprocessing tokens within a preprocessing directive (from just after the introducing `#` preprocessing token through just before the terminating new-line character) are space and horizontal-tab (including spaces that have replaced comments or possibly other white-space characters in translation phase 3).

The effect of this restriction is unclear, however, since translation phase 3 is permitted to transform all white space characters and comments into spaces. The relationship between these two rules should be clarified.

#### 925. Type of character literals in preprocessor expressions

Section: 15.2 [cpp.cond] Status: open Submitter: Michael Wong Date: 29 June, 2009

According to 15.2 [cpp.cond] paragraph 12,

> The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 7.7 [expr.const] using arithmetic that has at least the ranges specified in 17.3 [support.limits], except that all signed and unsigned integer types act as if they have the same representation as, respectively, `intmax_t` or `uintmax_t` (_N3035_.18.4.2 [stdinth]). This includes interpreting character literals, which may involve converting escape sequences into execution character set members.

Ordinary character literals with a single c-char have the type `char`, which is neither a signed nor an unsigned integer type. Although 7.3.7 [conv.prom] paragraph 1 is clear that `char` values promote to `int`, regardless of whether the implementation treats `char` as having the values of `signed char` or `unsigned char`, 15.2 [cpp.cond] paragraph 12 isn't clear on whether character literals should be treated as signed or unsigned values. In C99, such literals have type `int`, so the question does not arise. If an implementation in which plain `char` has the values of `unsigned char` were to treat character literals as unsigned, an expression like `'0'-'1'` would thus have different values in C and C++, namely -1 in C and some large unsigned value in C++.

#### 1436. Interaction of constant expression changes with preprocessor expressions

Section: 15.2 [cpp.cond] Status: open Submitter: Richard Smith Date: 2012-01-02

It appears that some of the recent changes to the description of constant expressions have allowed constructs into preprocessor expressions that do not belong there. Some changes are required to restrict the current capabilities of constant expressions to what is intended to be allowed in preprocessor expressions.

Proposed resolution (February, 2012):

1. Change 15.2 [cpp.cond] paragraph 8 as follows:

> Each preprocessing token that remains (in the list of preprocessing tokens that will become the controlling expression) after all macro replacements have occurred shall be in the lexical form of a token (5.10 [lex.token]). Any such token that is a literal (5.13.1 [lex.literal.kinds]) shall be an integer-literal, a character-literal, or a boolean-literal.

2. Change 15.2 [cpp.cond] paragraph 12 as follows:

> ...using arithmetic that has at least the ranges specified in 17.3 [support.limits]. The only operators permitted in the controlling constant expression are `?:`, `||`, `&&`, `|`, `^`, `&`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `<<`, `>>`, `-`, `+`, `*`, `/`, `%`, `!`, and `~`. For the purposes of this token conversion...

Additional note (March, 2022):

The proposed resolution shown above allows lambda-expressions, which is undesirable.

CWG 2022-11-11

A possible approach, in addition to the proposed resolution above, is to prohibit the `[` and `{` pp-tokens in the controlling expression of a conditional inclusion. C++ also permits using the alternative tokens, e.g. `not`, which might need clarification in the wording.

Additional notes (May, 2025)

See also issue 3017 for comma operators in the controlling expression of a conditional inclusion.

Additional notes (August, 2025)

See also submission #735 for the `<=>` operator.

#### 2190. Insufficient specification of `__has_include`

Section: 15.2 [cpp.cond] Status: open Submitter: Hubert Tong Date: 2015-10-24

The following example ought to be well-formed, but the standard is insufficiently clear:

      #define X <ciso646> )

      #if __has_include( X && ( defined(X) )
      #endif

Also, there is insufficient prohibition for the formation of `__has_include` by token pasting when performing macro replacement for the controlling expression of a conditional inclusion.

#### 2575. Undefined behavior when macro-replacing "defined" operator

Section: 15.2 [cpp.cond] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comment US 130 on the C++20 DIS.)

Subclause 15.2 [cpp.cond] paragraph 10 specifies:

> If the token `defined` is generated as a result of this replacement process or use of the `defined` unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined.

Undefined behavior during lexing is not acceptable. The behavior ought to be well-defined, ill-formed, or conditionally-supported.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1407, by decision of the CWG and SG12 chairs.

#### 3017. Commas in controlling expression of conditional inclusion

Section: 15.2 [cpp.cond] Status: open Submitter: Shafik Yaghmour Date: 2025-03-24

Consider:

      #if 1 ? 1, 0: 3
      #error
      #endif

Is this a well-formed translation unit?

According to 15.2 [cpp.cond] paragraph 10, C23 6.10.2 paragraph 3 and C23 6.6, the controlling expression is required to be a syntactic constant-expression (7.7 [expr.const]). (Concerns about C++ expressions vs. C expressions are handled via issue 1436.)

However, implementations uniformly reject the example (gcc and clang only in pedantic mode), because a comma operator appears in the controlling expression. There is no apparent normative basis for the rejection.

Do all implementations have the same bug, or do both C and C++ share the same specification hole?

Additional notes (March, 2025)

Forwarded to SG22 via paper issue #2294, by decision of the CWG chair.

Additional notes (May, 2025)

C23 specifies in its subclause 6.6 paragraph 3:

> Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.

Thus, the behavior of C implementations for the example is actually conforming. The remaining C++ concerns are handled by issue 1436.

#### 2576. Undefined behavior with macro-expanded `#include` directives

Section: 15.3 [cpp.include] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comment US 131 on the C++20 DIS.)

Subclause 15.3 [cpp.include] paragraph 4 specifies:

> If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined.

Undefined behavior during lexing is not acceptable. This ought to be ill-formed.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1408, by decision of the CWG and SG12 chairs.

#### 2947. Limiting macro expansion in pp-module

Section: 15.5 [cpp.module] Status: open Submitter: Jason Merrill Date: 2024-10-29

Consider:

      #define DOT_BAR .bar
      export module foo DOT_BAR;

The current rules appear to make this valid, referring to module `foo.bar` in phase 7. This ought to be ill-formed to align with the goal of keeping module directives parsable by simple tools.

Possible resolution:

1. Change in 15.5 [cpp.module] paragraph 1 as follows:

         pp-module:
             exportopt module pp-tokensopt ; new-line

2. Delete 15.5 [cpp.module] paragraph 3 as follows:

   > Any preprocessing tokens after the module preprocessing token in the module directive are processed just as in normal text. [Note 1: Each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens. —end note]

3. Change in 15.5 [cpp.module] paragraph 4 as follows:

   > The module and export (if it exists) preprocessing tokens are replaced by the module-keyword and export-keyword preprocessing tokens respectively. [Note: This makes the line no longer a directive so it is not removed at the end of phase 4. —end note] After this replacement, the preprocessing tokens that constituted the directive are a text-line and are processed as normal text. [Note: No macro expansion is possible for the pp-module-name and pp-module-partition. -- end note] Processing the remainder of the input shall produce a `;` or `[` preprocessing token following the pp-module-name and optional pp-module-partition.

4. Add 15.5 [cpp.module] paragraph 5 with examples:

   > [ Example:

   >       #define DOT_BAR .bar
   >       export module foo DOT_BAR;   // error: expansion of DOT_BAR; does not begin with ; or [

   > -- end example ]

   > [ Example:

   >       #define MOD_ATTR [[vendor::shiny_module]]
   >       export module M MOD_ATTR ;        // OK

   > -- end example ]

   > [ Example:

   >       export module a
   >       .b;                         // error: preprocessing token after pp-module-name is not ; or [

   > -- end example ]

   > [ Example:

   >       export module M [[
   >       attr1,
   >       attr2 ]] ;                 // OK

   > -- end example ]

   > [ Example:

   >       export module M
   >       [[ attr1,
   >       attr2 ]] ;                 // OK

   > -- end example ]

   > [ Example:

   >       export module M; int
   >       n;                         // OK

   > -- end example ]

#### 1718. Macro invocation spanning end-of-file

Section: 15.7 [cpp.replace] Status: open Submitter: David Krauss Date: 2013-07-23 Liaison: WG14

Although it seems to be common implementation practice to reject a macro invocation that begins in a header file and whose closing right parenthesis appears in the file that included it, there does not seem to be a prohibition of this case in the specification of function-style macros. Should this be accepted?

Notes from the February, 2014 meeting:

CWG agreed that macro invocations spanning file boundaries should be prohibited. Resolution of this issue should be coordinated with WG14.

#### 2577. Undefined behavior for preprocessing directives in macro arguments

Section: 15.7.1 [cpp.replace.general] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comment US 141 on the C++20 DIS.)

Subclause 15.7.1 [cpp.replace.general] paragraph 13 specifies:

> If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, [ Footnote: ...] the behavior is undefined.

Undefined behavior during lexing is not acceptable. The behavior ought to be ill-formed or conditionally-supported. Some implementation support an `#if` section, others reject that.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1413, by decision of the CWG and SG12 chairs.

#### 3053. Allowing `#undef likely`

Section: 15.7.1 [cpp.replace.general] Status: open Submitter: Jakub Jelinek Date: 2025-07-31

(From submission #734.)

P2843 moved a prohibition to `#define` keywords to the core language; it is now appearing in 15.7.1 [cpp.replace.general] paragraph 9:

> A translation unit shall not #define or #undef names lexically identical to keywords, to the identifiers listed in Table 4, or to the attribute-tokens described in 9.13 [dcl.attr], except that the names likely and unlikely may be defined as function-like macros.

This allows

      #define likely(x) x
      #define unlikely(a, b) a + b

but prohibits

      #undef likely
      #undef unlikely

even though guidelines for good macro hygiene suggest to `#undef` a macro past its region of use.

Possible resolution:

Change in 15.7.1 [cpp.replace.general] paragraph 9 as follows:

> A translation unit shall not #define or #undef names lexically identical to keywords, to the identifiers listed in Table 4, or to the attribute-tokens described in 9.13 [dcl.attr], except that the names likely and unlikely may be defined as function-like macros and may be undefined.

#### 1625. Adding spaces between tokens in stringizing

Section: 15.7.3 [cpp.stringize] Status: open Submitter: Chandler Carruth Date: 2013-02-18 Liaison: WG14

Given the following input,

      #define F(A, B, C) A ## x.B ## y.C ## z
      #define STRINGIFY(x) #x
      #define EXPAND_AND_STRINGIFY(x) STRINGIFY(x)
      char v[] = EXPAND_AND_STRINGIFY(F(a, b, c))

there is implementation variance in the value of `v`: some produce the string `"ax.by.cz"` and others produce the string `"ax. by. cz"`. Although 15.7.3 [cpp.stringize] paragraph 2 is explicit in its treatment of leading and trailing white space, it is not clear whether there is latitude for inserting spaces between tokens, as some implementations do, since the description otherwise is written solely in terms of preprocessing tokens. There may be cases in which such spaces would be needed to preserve the original tokenization, but it is not clear whether the result of stringization needs to produce something that would lex to the same tokens.

See also issue 2522.

Notes from the April, 2013 meeting:

Because the preprocessor specification is primarily copied directly from the C Standard, this issue has been referred to the C liaison for consultation with WG14.

#### 1709. Stringizing raw string literals containing newline

Section: 15.7.3 [cpp.stringize] Status: open Submitter: David Krauss Date: 2013-07-01

Stringizing a raw string literal containing a newline produces an invalid (unterminated) string literal and hence results in undefined behavior. It should be specified that a newline in a string literal is transformed to the two characters `'\' 'n'` in the resulting string literal.

A slightly related case involves stringizing a bare backslash character: because backslashes are only escaped within a string or character literal, a stringized bare backslash becomes `"\"`, which is invalid and hence results in undefined behavior.

#### 2578. Undefined behavior when creating an invalid string literal via stringizing

Section: 15.7.3 [cpp.stringize] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comment US 142 on the C++20 DIS.)

Subclause 15.7.3 [cpp.stringize] paragraph 2 specifies:

> If the replacement that results is not a valid character string literal, the behavior is undefined.

Undefined behavior during lexing is not acceptable. This ought to be ill-formed.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1409, by decision of the CWG and SG12 chairs.

#### 2522. Removing placemarker tokens and retention of whitespace

Section: 15.7.4 [cpp.concat] Status: open Submitter: Hubert Tong Date: 2018-04-08 Liaison: WG14

It appears that the replacement of a function-like macro following further rescanning and replacement may begin or end with whitespace when the leftmost or rightmost tokens were placemarker tokens. GCC and MSVC preserve the whitespace in both the starting and ending positions. Clang and ICC preserve only the trailing whitespace. For example:

      #define F( X ) X##X/**/>
      #define G( X ) !/**/X##X
      #define F0( X ) F(X)
      #define G0( X ) G(X)
      #define STR2( X ) #X
      #define STR( X ) STR2(X)
      #define Q
      static_assert(STR(>F0(Q))[1] == '\u0020', STR(>F0(Q)));
        // ^ Works with GCC and MSVC; fails with Clang and ICC.
      static_assert(STR(G0(Q)=)[1] == '\u0020', STR(G0(Q)=));

This appears to be an issue in C as well; see ISO C 6.10.3.

See also issue 1625.

#### 2579. Undefined behavior when token pasting does not create a preprocessing token

Section: 15.7.4 [cpp.concat] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comment US 143 on the C++20 DIS.)

Subclause 15.7.4 [cpp.concat] paragraph 3 specifies:

> If the result begins with a sequence matching the syntax of universal-character-name, the behavior is undefined. [Note: ... --end note] If the result is not a valid preprocessing token, the behavior is undefined.

Undefined behavior during lexing is not acceptable. The behavior ought to be well-defined, ill-formed, or conditionally-supported.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1410, by decision of the CWG and SG12 chairs.

Additional notes (November, 2023):

The first case of undefined behavior was addressed by paper P2621R2 (Undefined behavior in the lexer), adopted in June, 2023. The relevant part of 15.7.4 [cpp.concat] paragraph 3 now reads:

> ... [Note 1: Concatenation can form a universal-character-name (5.3.1 [lex.charset]). —end note] If the result is not a valid preprocessing token, the behavior is undefined. ...

#### 268. Macro name suppression in rescanned replacement text

Section: 15.7.5 [cpp.rescan] Status: open Submitter: Bjarne Stroustrup Date: 18 Jan 2001 Liaison: WG14

It is not clear from the Standard what the result of the following example should be:

>     #define NIL(xxx) xxx
>     #define G_0(arg) NIL(G_1)(arg)
>     #define G_1(arg) NIL(arg)
>     G_0(42)

The relevant text from the Standard is found in 15.7.5 [cpp.rescan] paragraph 2:

> If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file's preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

The sequence of expansion of `G0(42)` is as follows:

>     G0(42)
>     NIL(G_1)(42)
>     G_1(42)
>     NIL(42)

The question is whether the use of `NIL` in the last line of this sequence qualifies for non-replacement under the cited text. If it does, the result will be `NIL(42)`. If it does not, the result will be simply `42`.

The original intent of the J11 committee in this text was that the result should be `42`, as demonstrated by the original pseudo-code description of the replacement algorithm provided by Dave Prosser, its author. The English description, however, omits some of the subtleties of the pseudo-code and thus arguably gives an incorrect answer for this case.

Suggested resolution (Mike Miller): Replace the cited paragraph with the following:

> As long as the scan involves only preprocessing tokens from a given macro's replacement list, or tokens resulting from a replacement of those tokens, an occurrence of the macro's name will not result in further replacement, even if it is later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

> Once the scan reaches the preprocessing token following a macro's replacement list — including as part of the argument list for that or another macro — the macro's name is once again available for replacement. [Example:

>         #define NIL(xxx) xxx
>         #define G_0(arg) NIL(G_1)(arg)
>         #define G_1(arg) NIL(arg)
>         G_0(42)                         // result is 42, not NIL(42)

> The reason that `NIL(42)` is replaced is that `(42)` comes from outside the replacement list of `NIL(G_1)`, hence the occurrence of `NIL` within the replacement list for `NIL(G_1)` (via the replacement of `G_1(42)`) is not marked as nonreplaceable. —end example]

(Note: The resolution of this issue must be coordinated with J11/WG14.)

Notes (via Tom Plum) from April, 2004 WG14 Meeting:

Back in the 1980's it was understood by several WG14 people that there were tiny differences between the "non-replacement" verbiage and the attempts to produce pseudo-code. The committee's decision was that no realistic programs "in the wild" would venture into this area, and trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs.

Additional notes (June, 2022):

It is unclear whether the phrasing "following the macro's replacement list" in the suggested resolution covers this case:

      #define f(x) b
      #define b(x) x(1)

      f(1)(f) // the result is 1

#### 2580. Undefined behavior with `#line`

Section: 15.8 [cpp.line] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comments US 144 and US 145 on the C++20 DIS.)

Subclause 15.8 [cpp.line] paragraph 3 specifies:

> If the digit sequence specifies zero or a number greater than 2147483647, the behavior is undefined.

Subclause 15.8 [cpp.line] paragraph 5 specifies:

> If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined; otherwise, the result is processed as appropriate.

Undefined behavior during lexing is not acceptable. This ought to be ill-formed.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1411, by decision of the CWG and SG12 chairs.

#### 2693. Escape sequences for the string-literal of `#line`

Section: 15.8 [cpp.line] Status: open Submitter: CWG Date: 2023-02-07 Liaison: WG14

The grammar for `#line` is inconsistent between 15.8 [cpp.line] paragraph 4:

>     # line digit-sequence " s-char-sequenceopt " new-line

and 15.8 [cpp.line] paragraph 1:

> The string-literal of a `#line` directive, if present, shall be a character string literal.

The characters comprising the string-literal are not necessarily interpreted for e.g. escape sequences or universal-character-names, so the use of string-literal here is questionable. This directive names a source file, thus it might be better to use header-name (5.6 [lex.header]) instead.

#### 1889. Unclear effect of `#pragma` on conformance

Section: 15.10 [cpp.pragma] Status: open Submitter: James Widman Date: 2014-03-05

According to 15.10 [cpp.pragma] paragraph 1, the effect of a `#pragma` is to cause

> the implementation to behave in an implementation-defined manner. The behavior might cause translation to fail or cause the translator or the resulting program to behave in a non-conforming manner.

It should be clarified that the extent of the non-conformance is limited to the implementation-defined behavior.

#### 2581. Undefined behavior for predefined macros

Section: 15.12 [cpp.predefined] Status: open Submitter: US Date: 2019-10-23 Liaison: SG12

(From National Body comments US 148 and US 149 on the C++20 DIS.)

Subclause 15.12 [cpp.predefined] paragraph 4 specifies:

> If any of the pre-defined macro names in this subclause, or the identifier `defined`, is the subject of a `#define` or a `#undef` preprocessing directive, the behavior is undefined.

Undefined behavior during lexing is not acceptable. This ought to be ill-formed.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1412, by decision of the CWG and SG12 chairs.

#### 897. `_Pragma` and extended string-literals

Section: 15.13 [cpp.pragma.op] Status: open Submitter: Daniel Krügler Date: 9 May, 2009

The specification of how the string-literal in a `_Pragma` operator is handled does not deal with the new kinds of string literals. 15.13 [cpp.pragma.op] says,

> The string literal is destringized by deleting the `L` prefix, if present, deleting the leading and trailing double-quotes, replacing each escape sequence...

The various other prefixes should either be handled or prohibited.

Additional note (October, 2013):

If raw string literals are supported, the question of how to handle line splicing is relevant. The wording says that “the characters are processed through translation phase 3,” which is a bit ambiguous as to whether that includes phases 1 and 2 or not. It would be better to be explicit and say that the processing of phase 3 or of phases 1 through 3 is applied.

See also the near-duplicate issue 2694.

#### 2694. string-literals of the `_Pragma` operator

Section: 15.13 [cpp.pragma.op] Status: open Submitter: CWG Date: 2023-02-07 Liaison: WG14

Subclause 15.13 [cpp.pragma.op] paragraph 1 specifies:

> A unary operator expression of the form:

>           _Pragma ( string-literal )

> is processed as follows: The string-literal is destringized by deleting the `L` prefix, if present, deleting the leading and trailing double-quotes, replacing each escape sequence `\"` by a double-quote, and replacing each escape sequence `\\` by a single backslash. The resulting sequence of characters is processed through translation phase 3 to produce preprocessing tokens that are executed as if they were the pp-tokens in a pragma directive. The original four preprocessing tokens in the unary operator expression are removed.

In contrast, C23 section 6.10.6 specifies:

> A unary operator expression of the form:

>         _Pragma ( string-literal )

> is processed as follows: The string literal is destringized by deleting any encoding prefix, ...

While in C, any encoding prefix is deleted, C++ only deletes the L prefix and does not consider UTF-8, UTF-16, and UTF-32 string literals. This was probably an oversight when C++ obtained UTF-x string literals.

However, a string-literal entails lexing of escape sequences and universal-character-names, which seems not useful given that the string literal is de-stringized immediately afterwards. It might be more appropriate to employ a simpler lexical structure such as a q-char-sequence as used in a header-name instead. See also issue 2693.

See also issue 897, which is also concerned with raw string literals and line splicing.

#### 2784. Unclear definition of member-designator for `offsetof`

Section: 17.2.4 [support.types.layout] Status: open Submitter: Corentin Jabot Date: 2023-08-21 Liaison: EWG

Consider:

      struct S {
        int a;
      };
      int x = offsetof(S, S::a)

Is this valid? MSVC and gcc appear to support qualified-ids in the member-designator of `offsetof`; clang does not. Once that question is clarified, the rules about value-dependence may need to be amended, because a qualified-id might introduce type-dependence.

Specific questions:

- Is a qualified-id allowed to appear in a member-designator?
- Is a template-id allowed to appear in a member-designator?
- Is an `offsetof` expression a core constant expression (7.7 [expr.const])?

2023-08-23

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

EWG 2023-11-07

EWG is soliciting a paper to thoroughly explore the design space.

#### 2361. Unclear description of `longjmp` undefined behavior

Section: 17.14.3 [csetjmp.syn] Status: open Submitter: Zhihao Yuan Date: 2017-10-20

According to 17.14.3 [csetjmp.syn] paragraph 2,

> A `setjmp`/`longjmp` call pair has undefined behavior if replacing the `setjmp` and `longjmp` by `catch` and `throw` would invoke any non-trivial destructors for any automatic objects.

The intent is clear, that transferring control from point A to point B via `longjmp` has undefined behavior if throwing an exception at point A and catching it at point B would invoke non-trivial destructors. The wording could be more precise.

See also the corresponding editorial issue for additional discussion.

Notes from the October, 2018 teleconference:

There are a number of unanswered questions in the current wording, including the impact on the current exception (whether it still exists) after a `longjmp` out of a handler, the impact on the initialization of locks if jumping from the initialization of a local static data member, etc. One thought was to restrict use of `longjmp` to “plain C functions”. Another was to say if the program would have different behavior via the use of exceptions the behavior is undefined. There was no consensus on how to proceed.

Additional note, January, 2022:

See also library issues 1265 and 3652.

#### 1944. New C incompatibilities

Section: Clause Annex C [diff] Status: open Submitter: Mike Miller Date: 2014-06-18

Some new features of C++ not only introduce incompatibilities with previous versions of C++ but also with C; however, the organization of Annex Clause Annex C [diff] makes it difficult to specify that a given feature is incompatible with both languages, and the practice has been only to document the C++ incompatibilities. Some means of specifying both sets of incompatibilities should be found, hopefully without excessive duplication between the C and C++ sections.

#### 1279. Additional differences between C++ 2003 and C++ 2011

Section: C.6 [diff.cpp03] Status: open Submitter: Nikolay Ivchenkov Date: 2011-03-27

A number of differences between C++03 and C++11 were omitted from C.6 [diff.cpp03]:

- New keywords. Although these are said in C.6.2 [diff.cpp03.lex] only to invalidate C++03 code, they can also change the meaning, e.g., `thread_local x(y)`, which would declare a variable `thread_local` initialized with `y` in C++03 and a thread-local variable `y` in C++11.

- New deduction rules.

- Removal of the deprecated string literal conversion.

- When a `friend` function defined in a class template is actually defined (i.e., with each instantiation or only when odr-used).

- Removal of access declarations.

- Use of the injected-class-name of a class template as a template template-argument.

Additional note (January, 2012):

In addition to the items previously mentioned, access declarations were removed from C++11 but are not mentioned in C.6 [diff.cpp03].

Proposed (partial) resolution (February, 2012):

Add the following as a new section in C.6 [diff.cpp03]:

> C.2.5 11.8 [class.access]: member access control | pdiff.cpp03.class.access

> Change: Remove access declarations.

> Rationale: Removal of feature deprecated since C++ 1998.

> Effect on original feature: Valid C++ 2003 code that uses access declarations is ill-formed in this International Standard. Instead, using-declarations (9.10 [namespace.udecl]) can be used.

#### 1248. Updating Annex C to C99 and C23

Section: C.7 [diff.iso] Status: open Submitter: Jonathan Wakely Date: 2011-02-28

The description of incompatibilities with C in Annex C.7 [diff.iso] is based on C89, but there are a number of new features in C99 and C23 that should be covered.

Additional note (August, 2024)

The issue title and description were amended to cover C23 as well.

#### 3008. Missing Annex C entry for `void` object declarations

Section: C.7.6 [diff.dcl] Status: open Submitter: Jiang An Date: 2025-03-13

(From submission #682.)

C allows

      extern void x;

C++ does not, as clarified by issue 2475 in 9.1 [dcl.pre] paragraph 8.

This deviation warrants an entry in Annex C.

Possible resolution:

Add a new paragraph to C.7.6 [diff.dcl] as follows:

> Affected subclause: 9.1 [dcl.pre]
> Change: In C++, no declaration of a variable can have `void` type. In C, this is allowed, unless the declaration is a definition.
> [Example:

>       extern void x;   // valid C, invalid in C++

> -- end example]
> Rationale: Stricter type checking in C++.
> Effect on original feature: Deletion of semantically well-defined feature.
> Difficulty of converting: Syntactic transformation.
> How widely used: Seldom.