I have an example1 and an example2, which uses a forward declaration of a class template prefixed with the namespace of the corresponding class template. The first example compiles fine with visual-studio, while the second example does not. I checked the both examples against other compilers (http://rextester.com). Now I have 2 questions:
Using here a namespace in the forward declaration seems to be illegal. Why exactly?
Visual studio (2015 and 2017) seems to allow the forward declaration with the additional namespace in the first example, but in the second example not. Is this a bug?
Example 1:
#include <vector>
namespace N1
{
template <typename T> struct MySystem {};
template <typename T> class Other {};
struct MyClass
{
MyClass() { typename Dependencies::TYPE_A oTYPE_A; }
struct Dependencies
{
template <typename>
class N1::Other;
struct TypeX_Dependencies;
using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;
struct TypeX_Dependencies
{
using TYPE_A = typename Dependencies::TYPE_A;
};
using TYPE_X = N1::Other<TypeX_Dependencies>;
};
};
}
int main(){ return 0; }
c++ (gcc 5.4.0)
source_file.cpp:15:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;
c++ (clang 3.8.0)
source_file.cpp:15:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:15:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;
c++ (vc++ 19.00.23506 for x64 / also vs community 2017 15.4.4)
compiles fine
Example 2:
#include <vector>
namespace N1
{
template <typename T> struct MySystem {};
template <typename T> class Other {};
template <typename T>
struct MyClass
{
MyClass() { typename Dependencies::TYPE_A oTYPE_A; }
struct Dependencies
{
template <typename>
class N1::Other;
struct TypeX_Dependencies;
using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;
struct TypeX_Dependencies
{
using TYPE_A = typename Dependencies::TYPE_A;
};
using TYPE_X = N1::Other<TypeX_Dependencies>;
};
};
}
int main(){ return 0; }
c++ (gcc 5.4.0)
source_file.cpp:16:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;
c++ (clang 3.8.0)
source_file.cpp:16:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:16:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;
c++ (vc++ 19.00.23506 for x64 / also vs community 2017 15.4.4)
source_file.cpp(16): error C3855: 'N1::Other': template parameter 'T' is incompatible with the declaration
source_file.cpp(24): note: see reference to class template instantiation 'N1::MyClass<T>::Dependencies' being compiled
source_file.cpp(29): note: see reference to class template instantiation 'N1::MyClass<T>' being compiled
source_file.cpp(20): error C3203: 'Other': unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type
Yes, this is a bug.
According to [dcl.type.elab]/1,
... If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization, an explicit instantiation or it has one of the following forms:
- class-key attribute-specifier-seqopt identifier ;
friend
class-key::
opt identifier ;friend
class-key::
opt simple-template-id ;friend
class-key nested-name-specifier identifier ;friend
class-key nested-name-specifiertemplate
opt simple-template-id ;
your declaration class N1::Other
is neither an explicit specialization, nor an explicit instantiation, so it has to have the emphasized form (ignoring those friend
declaration). Note no nested-name-specifier is allowed before identifier in the emphasized form, so the declaration is ill-formed. The compiler error of Clang in the following example without template shows this problem.
namespace N {
struct S;
}
struct N::S; // error: forward declaration of struct cannot have a nested name specifier
LIVE EXAMPLE (BTW, GCC accepts this code and just gives a warning about nothing new to be declared. I guess it is a bug of GCC.)
Here the template-head template <typename>
does not help, because class N1::Other
itself should form a declaration according to the grammar definition of template-head, thus the paragragh above applies.
Roughly speaking, a declaration for class in different scope from the scope where a class with the same name is declared should either introduce a new class, in which case nested-name-specifier should not be used, or define the previously declared class, in which case the grammar forms a class-specifier rather than an elaborated-type-specifier, thus the paragraph above does not apply. As a conclusion, this rule is reasonable.