https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108169
mail at jhellings dot nl changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |mail at jhellings dot nl --- Comment #3 from mail at jhellings dot nl --- I looked a bit further into this and into what the standard says. GCC seems to derive the wrong type for non-type template parameters in a few cases both (1) when they have a placeholder type such as auto and (2) when they use a normal type. See https://jhellings.nl/article?articleid=1 for the full analysis. My findings (summary): Let V be a non-type template parameter. The C++20 standard defines decltype(V) as follows (Clause 1 of Section [dcl.type.decltype]): "For an expression E, the type denoted by decltype(E) is defined as follows: ... otherwise, if E is an unparenthesized id-expression naming a non-type template-parameter, decltype(E) is the type of the template-parameter after performing any necessary type deduction;" In the above, the type of a template-parameter is defined as follows (Clause 6 of Section [temp.param]): "A non-type template-parameter shall have one of the following (possibly cv-qualified) types: ... The top-level cv-qualifiers on the template-parameter are ignored when determining its type." Hence, decltype(V) with V a non-type template parameter should never yield a type with a top-level const qualifier. In GCC 12 (x86-64 gcc 12.2 on Compiler Explorer), GCC does not follow this rule in the following case: if the type of V is a structural class type T, then GCC always yields a ``const T''. See, for example, the following program _that does not use placeholder types_: /* * @author{Jelle Hellings}. */ #include <iostream> #include <typeinfo> #include <type_traits> /* * Format the type @{Type} and print it to standard output. The qualifiers * @{qs...} are used to include further details on @{Type}. */ template<class Type> void type_printer(const auto&... qs) { if constexpr (std::is_lvalue_reference_v<Type>) { return type_printer<std::remove_reference_t<Type>>(qs..., "&"); } if constexpr (std::is_rvalue_reference_v<Type>) { return type_printer<std::remove_reference_t<Type>>(qs..., "&&"); } if constexpr (std::is_const_v<Type>) { return type_printer<std::remove_const_t<Type>>(qs..., "const"); } if constexpr (std::is_volatile_v<Type>) { return type_printer<std::remove_volatile_t<Type>>(qs..., "volatile"); } if constexpr (std::is_array_v<Type>) { return type_printer<std::remove_extent_t<Type>>(qs..., "[]"); } if constexpr (std::is_pointer_v<Type>) { return type_printer<std::remove_pointer_t<Type>>(qs..., "*"); } ((std::cout << qs << " "), ...); std::cout << '`' << typeid(Type).name() << '`' << std::endl; } /* * Print the details on the type @{Type}, the type of a non-type template * parameter with value @{Value}, and on the type of expressions involving * @{Value}. The printed data will be labeled with @{case_name}. */ template<class Type, Type Value = Type{}> void inspect_template_argument(const auto& case_name) { type_printer<Type>(case_name); type_printer<decltype(Value)>(case_name); } /* * A dummy struct type. */ struct dummy {}; /* * Entry-point of the program. */ int main() { /* We look at most combinations of types and values. */ inspect_template_argument<int>("{int}"); inspect_template_argument<const int>("{c-int}"); inspect_template_argument<dummy>("{dummy}"); inspect_template_argument<const dummy>("{c-dummy}"); } The above program SHOULD return: {int} `int` {int} `int` {c-int} const `int` {c-int} `int` {dummy} `5dummy` {dummy} `5dummy` {c-dummy} const `5dummy` {c-dummy} `5dummy` But, in x86-64 gcc 12.2 (on Compiler Explorer), it returns {int} `i` {int} `i` {c-int} const `i` {c-int} `i` {dummy} `5dummy` {dummy} const `5dummy` << WRONG to the best of my understanding {c-dummy} const `5dummy` {c-dummy} const `5dummy` << WRONG to the best of my understanding Note that in x86-64 gcc 11.3 (on Compiler Explorer), the output was more-wrong, as also the const-qualifier for non-class types was preserved in some cases. Indeed, x86-64 gcc 11.3 (on Compiler Explorer) returns: {int} `i` {int} `i` {c-int} const `i` {c-int} const `i` << WRONG & FIXED to the best of my understanding {dummy} `5dummy` {dummy} const `5dummy` << WRONG to the best of my understanding {c-dummy} const `5dummy` {c-dummy} const `5dummy` << WRONG to the best of my understanding The issues detected above carry over to the usage of placeholder types, leading to the bug reported in this bug report.