Reputation: 8807
I wonder if anyone knows why the following two pieces of code behave very differently. I can understand why the second one doesn't work, but why does the first one work? At the same place int x = gc.f();
the template should get instantiated so the same error would occur, but why actually there is no error?
a.cpp
#include <iostream>
using namespace std;
template <typename T>
struct A {
struct B {
};
};
template <typename T>
struct C {
typedef A<C<T> > D;
int f() {
typename D::B p;
return 0;
}
};
C<float> gc;
int x = gc.f();
template <typename T>
struct A<C<T> > {
struct B {
B() {
cout << "B::B()" << endl;
}
~B() {
cout << "B::~B()" << endl;
}
};
};
int main() {
}
output
B::B()
B::~B()
and
a2.cpp
#include <iostream>
using namespace std;
template <typename T>
struct A {
struct B {
};
};
struct C {
typedef A<C> D;
int f() {
D::B p;
return 0;
}
};
C gc;
int x = gc.f();
template <>
struct A<C> {
struct B {
B() {
cout << "B::B()" << endl;
}
~B() {
cout << "B::~B()" << endl;
}
};
};
int main() {
}
compiler error
a2.cpp:24: error: specialization of ‘A<C>’ after instantiation
a2.cpp:24: error: redefinition of ‘struct A<C>’
a2.cpp:6: error: previous definition of ‘struct A<C>’
Upvotes: 4
Views: 388
Reputation: 208466
You are actually asking for two different things, although both related to template instantiation.
Why does the first piece of code compile?
The standard states that the actual instantiation of the template is performed after the whole translation unit is processed, which means that the real instantiation of the template will be after all types defined in that translation unit are complete, even if the point of instantiation is different and much earlier in the translation unit. More on this in this other question
Why does the second example not compile?
The problem with the second example is that the standard requires that an specialization of a template must be declared before the first use of that specialization.
§14.7.3p6 (C++03)
If a template, a member template or the member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantia- tion to take place, in every translation unit in which such a use occurs; no diagnostic is required.
Note that there are two different concepts here. The point of instantiation refers to where in the code the instantiation takes place, not when it is instantiated. In your example the point of instantiation is the expression C<float> gc;
, while the when as in all other cases is after the whole translation unit is processed.
Upvotes: 3
Reputation: 13536
You just have to remove the unnecessary typename qualifier and rearrange things a bit. As 2nd the error message says, you have specialized A<C>
after it's already been instantiated in C
. To fix this you can move the specialization of A<C>
up to before the definition of C
and then forward declare C
to get rid of the undeclared identifier error.
Here's the fixed code:
#include <iostream>
using namespace std;
template <typename T>
struct A {
struct B {
};
};
struct C; // Forward declaration of C
template <>
struct A<C> {
struct B {
B() {
cout << "B::B()" << endl;
}
~B() {
cout << "B::~B()" << endl;
}
};
};
struct C {
typedef A<C> D;
int f() {
D::B p;
return 0;
}
};
C gc;
int x = gc.f();
int main() {
}
Upvotes: 2
Reputation: 272812
Because typename
is only to be used when indicating types from within a template.
Upvotes: 1