问题
Given the following class template:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
we define Inner
separately for each specialization of Outer
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
and then define the member function f
once for all specializations of Outer
:
auto Outer<T>::f(Inner) -> void
{
}
but Clang (9.0.0) complains:
error: variable has incomplete type 'Outer::Inner'
auto Outer<T>::f(Inner) -> void
^
We can evade the compiler error by also providing a definition of Inner
for all other specializations of Outer
:
template<typename T>
struct Outer<T>::Inner {};
or by defining f
separately for each specialization:
template<>
auto Outer<int>::f(Inner) -> void
{
}
template<>
auto Outer<double>::f(Inner) -> void
{
}
Both GCC and MSVC accept the initial code, which begs the question; is this a Clang bug or is it the only conformant implementation out of the three?
Try on Compiler Explorer
回答1:
I believe Clang is wrong to reject your code. We must ask ourselves, how does your function declaration and definition compare to
auto f(typename T::Inner) -> void;
// ...
template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }
In this example, T::Inner
is obviously a dependent type. So Clang may not assume it's incomplete until instantiation. Does the same hold true in your example? I would say so. For we have this in the standard:
[temp.dep.type]
5 A name is a member of the current instantiation if it is
- An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof. [ Note: This can only occur when looking up a name in a scope enclosed by the definition of a class template. — end note ]
- ...
A name is a dependent member of the current instantiation if it is a member of the current instantiation that, when looked up, refers to at least one member of a class that is the current instantiation.
9 A type is dependent if it is
- ...
- a member of an unknown specialization,
- a nested class or enumeration that is a dependent member of the current instantiation,
- ...
So the first bullet in paragraph 9 covers the case typename T::Inner
. That is a dependent type.
Meanwhile your case is covered by the second bullet. Outer::Inner
is a name that is found in the current instantiation of Outer
, moreover it's found inside Outer
itself, and not in a base class. That makes it a dependent member of the current instantiation. This name refers to a nested class. Which means all the conditions in the second bullet apply, thus making Outer::Inner
a dependent type as well!
Since we have ourselves a dependent type in both cases, compilers should treat them equally as dependent types. My conclusion is that GCC and MSVC are right.
来源:https://stackoverflow.com/questions/58936617/is-clang-correct-to-reject-code-in-which-the-nested-class-of-a-class-template-is