A Bite of Template in C++ (2)
24 Jun 2018 Languages C/C++ metaprogrammingI encountered two issues when using C++ template.
One is about why sometimes typename
has to be prefixed to declarations,
and another is how to access the types in the parameter list from outside of the template.
This post has some ideas for those two questions,
as well as some discussions about nested templates.
The Keyword typename
in Declarations
Qualified names and dependent names
Qualified name refers to a name with explicitly specified scope prefixed,
and the scope could be namespace, struct, class.
For example, std::cout
is a qualified name, printf
is an unqualified name,
and vector<int>::iterator
is a qualified name.
Dependent name refers to a name dependent on a template parameter. For instance:
In the above template class, non_depedent_i
, non_depedent_vi
,
and non_depedent_pvi
are non-depedent names,
while depedent_i
, depedent_vi
, and depedent_pvi
are depedent names.
The motivation to require typename
in some declarations
It is highly likely that there are operations on depedent names inside a template.
The depedent names are in “quantum state” when compiler evaluates the declaration of the template, and they do not “collapse” until instantiation.
Naturally, some errors are not able to detect during evaluating the declaration,
like whether the type T
has a member called x
,
so compiler has to check one more time upon instantiation.
However, there could be ambiguities occuring during instantiation.
In the following code, both class Value
and class Type
have a member x
.
The difference is that in Value
x
is a constant, while in Type
x
is a type.
For the statement T::x * Pi
inside the template function accessValue()
,
either a value x
or a type x
makes sense.
The statement becomes a multiplication for class Value
,
and declares a variable for class Type
.
After analysis, we find the ambiguities actually happen when the depedent names are used as scope name, which followed by scope resolution operator ::
.
Disambiguation for declarations involving depedent scopes
The solution is C++ standard requires a disambiguator, typename
, before depedent scope,
to explicitly claim the member would be regarded as type.
Implicitly, the names after depedent scope without the disambiguator typename
would be treated as value or variable whenever it is possible.
Here comes more examples:
In addition to typename
disambiguator in some declarations,
the keyword template
could appear as disambiguator in some declarations as well.
It has the similar motivation and solutions.
See the following code for elaboration:
Template Parameter List Exposure
Visibility of templete parameter list
The type passed to template could be a template class,
then comes the question:
is there are way for the template to know what type is used in the template class?
Here is a fragment of code about what I am questioning:
The compiler would complain “no type named ‘T’ in ‘class Inner
Look at the statement Outer< Inner<int> > obj;
.
Compiler instantiates the class Inner<int>
first,
then instantiates the class Outer< Inner<int> >
,
and eventually creates the object obj
.
During the first instantiation of Inner<int>
,
all T
s inside the Inner
template are substituted with int
.
When it comes to the turn for Outer< Inner<int> >
to instantiate,
there is no more T
left in the Inner<int>
.
Definitely, compiler cannot find any type or variable or member function
called T
inside Inner<int>
.
Therefore, theoretically types in the template parameter list is not visible outside the template.
Make them visible
There are two possible ways to make template types as “visible”,
using typedef
specifer or type alias.
It is straightforward to using typedef
to expose types passed from parameter list.
For example:
Note that the typedef
statements must be in the public section.
Type alias is introduced since C++11.
It uses the keyword using
.
Here comes an example that is equivalent to the previous typedef
example:
Shadow and nest
However, the two solutions mentioned above are not perfect. As they create the new alias for outside access, they use new names for types. Instantiation is like simple substitution, and will not analysis to distinguish alias from the origin. If you try to keep the same name, then the compiler would complain like “shadows template parm ‘class Vertex_t’”. Furthermore shadow is even forbidden in nested scopes as well. Like the following code would not get compiled due to errors caused by shadow:
Although from my perspective, the ban is to save some effort to design compiler, here is an example from the stackoverflow thread Accessing template parameters of the member templates to justify the necessity:
The code has be heavily modified to get compiled and elaborate the idea.
According to their post, the authors assume the template of Inner
can use T1
,
thus shadows the T1
in Outer
,
then res1
should become true, which seems weird to the authors.
By the way, I failed to use the object out_bool
to instantiate Inner
template.
As following code shows, I used knowledge in the first part of this post,
trying to make it explicit, but none of them work.
Finally, I would like to mention the part 2 of their stackoverflow post is saying: partial specialization is allowed for nested templates, and the specialization could be the situation that the outer template and the inner template share a same type or types.
For example, if the struct Inner
in the previous example takes more than one parameters,
then we can define a partial specialization like follows:
Future Work
As the “blood” of metaprogramming, template has too much points to cover in one or two posts. I am not sure what would be my next post. It depends on what problem I encounter in the practice, I think.
Credits
- A Description of the C++ typename keyword - Thank Even Driscoll for your article, where I first time realize the necessity of adding typename in variable declaration.
- David Abrahams and Aleksey Gurtovoy - Thanks to your book “C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond”, where I learnt a lot about metaprogramming.
- typedef specifier
- Type alias, alias template
- Accessing template parameters of the member templates - Thank jogojapan and Kirill Kobelev for your big work.