"A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule." The diagnostic for the rule is not required, but since GCC diagnoses some of the cases, there is a bugreport about missing diagnostic cases (bug 46983), and the check might become mandatory (will it?), I'd like file this bug. GCC accepts the code with alias templates: struct S {}; template <typename> struct T {}; struct U { template<typename> using S = S; template<typename> using T = T<void>; }; but produces the "declaration changes meaning" error for type aliases. GCC also diagnoses the case if the "outer" declaration is used before the corresponding alias template: struct S {}; struct U { S s; template<typename> using S = S; // error: declaration of ... changes meaning of 'S' }; (From https://stackoverflow.com/q/63369264/)
Like you say, this might be mandatory in C++23: https://wg21.link/p1697r0. So confirmed.
*** Bug 102444 has been marked as a duplicate of this bug. ***
From 102444 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102444): See http://eel.is/c++draft/class.member.lookup#6 [class.member.lookup]/p6: "If it [the result of the search] differs from the result of a search in T for N from immediately after the class-specifier of T, the program is ill-formed, no diagnostic required." A class definition is not allowed to change the meaning of a name that was already used earlier in the class definition. While no diagnostic is required, I think a quality implementation like gcc should produce a diagnostic here and reject this program since it is ill-formed. Example (found in the wild): https://gcc.godbolt.org/z/jhEj68Kq8 struct plus { template<typename... Args> using invoke = void; }; template <typename Fn, typename... Args> using invoke = typename Fn::template invoke<Args...>; template <typename Fn> struct compose { template <typename X, typename Y> using F = invoke<Fn, X, Y>; template <typename X> using invoke = invoke<Fn, X, X>; }; using Q = compose<plus>::F<int, int>; Is accepted by gcc.