Introduction
This article is targeted at those interested in C++ templates and have already made some experiences with them.
The attached code sample was tested in VC++ 2008 SP1 and GCC (MinGW) 4.5.2. A VS2008 workspace and a batch file "compile.bat" for compiling the code sample in GCC is included.
The code sample does not produce an application, it is only intended to show the reaction of the compiler on certain code sequences. By default, there should be no compiler errors. Certain definitions can be enabled (e.g., BAD_PRACTICE_1A
) to show the compiler's reaction on certain non C++ standard compliant code. All samples are explained in the following article.
The topics contained in this article refer to my experiences when correcting the code from my CodeProject article "Enumerator Lists and Enumerated Arrays" in order to compile using GCC. I do not intend to give a full review about non-standard compliant behavior of VC++ 2008 concerning templates, this is just a summary of topics that occurred to me. They might be helpful when creating portable C++ code.
It is important to understand that each C++ compiler represents a different "C++ dialect". Therefore it is possible to slide into coding practices that are not standard-compliant and also not recognized by other compilers.
Background
warning C4346: '...' : dependent name is not a type
When writing my article Enumerator Lists and Enumerated Arrays I got that a lot. And after the warning, there always was an error of some sort. Using MSDN help, I quickly figured I was missing a typename
and added that whenever in doubt. Finally, I ended up doing things like this (okay, the example is exaggerated):
template<typename t>
struct a {
typename int x;
};
This is OK by Visual C++ 2008 standards (independent of setting the \za compiler flag which disables Microsoft language extensions). Anyway, the code is not portable. The int
type is not dependent and should not be declared as such. When I tried to compile my project with GCC (MinGW), I got plenty of compiler errors such as:
error: expected nested-name-specifier before '...'
Well, great! Let's try and see what it is all about dependent type names.
What is a dependent name?
On open-std.org, I found the following definition [1]:
A type is dependent if it is:
- a template parameter,
- a member of an unknown specialization,
- a cv-qualified type where the cv-unqualified type is dependent,
- a compound type constructed from any dependent type,
- an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent,
- a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent.
[Note: Because typedef
s do not introduce new types, but instead simply refer to other types, a name that refers to a typedef
that is a member of the current instantiation is dependent only if the type referred to is dependent.]
This actually defines the term "dependent type", not "dependent name". For the latter, I found the following definitions:
- IBM: A dependent name is a name that depends on the type or the value of a template parameter [2]. (Actually, here the fact is missed that the template parameter is also a dependent name.)
- Microsoft: Names that depend in some way on the template arguments, referred to as dependent names[3].
As a result, dependent names may refer to dependent types, but may also to something else.
C++ Standard: A name is a use of an identifier that denotes an entity or label. Every name that denotes an entity is introduced by a declaration.[4].
Names therefore refer to probably anything created by the programmer. E.g., variables, classes, functions, and namespaces are referred to by variable names, class names, function names, and namespace names.
So, when do we have to use typename to declare dependent types?
The following sample comprises some possible contents of a class template:
template<typename t>
struct a {
t x;
typedef typename t::value_type value_type;
value_type v;
int i;
void f() {
t::value = 5; t::f(); }
};
Generally speaking, when the compiler does not know that a dependent name refers to a type but we want to use it as such. Consider the following rules of thumb:
- When coming across a dependent name, it first assumes it as anything else but a type. It is necessary to prefix it by
typename
. - When writing a
::
inside a template, look if the names left of it refer to at least one dependent name. If that is the case and the name right to it also refers to a type, typename
is needed. - If the name has already been identified as a type in the template, do not use
typename
again on it (see types t
and value_type
in the previous example). - Get a second opinion from another compiler. Visual C++ nowadays claims to be mostly standard-compliant. However, as seen, it not only compiles a sub-set of the standard, but also non-compliant code.
The whole issue revolves around two topics:
- When a C++ compiler parses a class template, it already performs syntax-checking. Namespaces are resolved, types and values are separated, etc. It is ensured that it would be possible to instantiate the template with some template parameters. Or at least it should be.
- Being able to use the template with another compiler is the other thing.
What else is out there (related to dependent names)?
Actually a lot. I came around the following issues in my project:
VC++ allows dependent types to be specified without typename
Before VS2003, VC++ did not require dependent types to be specified at all [5]. (VS2008) VC++ still sometimes does not require the typename
keyword where it is necessary [6].
The following example provides a function template where normally the same rules would be applied as in class templates. It compiles in VC++ 2008 without the typename
as well (the same effect applies to methods within class templates).
namespace example2
{
template <typename T>
void f(const T& obj)
{
#ifdef BAD_PRACTICE_2
for (T::const_iterator i = obj.begin(); i != obj.end(); i++) {
#else
for (typename T::const_iterator i = obj.begin(); i != obj.end(); i++) {
#endif
}
}
}
In GCC 4.5.2, the following error is emitted:
In function '...':
error: need 'typename' before '...' because '...' is a dependent scope
...
Calling the explicit instantiation of a static function template in a class template from another template
Huh, that was long. Look at the following example:
namespace example3
{
template<typename t> struct a {
template<typename t2> static void af(t2 i) {
i *= 2;
}
};
template<typename t>
void f() {
a<t>::af(0);
#ifdef BAD_PRACTICE_3
a<t>::af<int>(0);
#else
a<t>::template af<int>(0); #endif
}
}
Again, this sample compiles in VC++ 2008, but GCC 4.5.2 will emit the following error:
In function '...':
error: expected primary-expression before '...'
...
Template namespace resolution in function templates
The following code sample will compile in VC++ 2008. The critical line is a<t> aa;.
At this code position, it should not be possible to resolve a
as it is contained in namespace example4a
which is not introduced into the declarative region at this point. It seems that VC++ compiles the contents of example4b::f()
with the namespace resolution at the time the function is instantiated. The namespace inclusion for example4a
is available at the point of instantiation but not when the function template is defined.
namespace example4
{
namespace example4a
{
template<typename t>
struct a {
t value;
};
}
namespace example4b
{
template<typename t>
void f() {
#ifdef BAD_PRACTICE_4
a<t> aa;
#else
example4a::a<t> aa;
#endif
aa.value = 5;
}
}
#ifdef BAD_PRACTICE_4
using namespace example4a;
#endif
void f() {
example4b::f<int>();
}
}
In GCC 4.5.2, the following error will be emitted when compiling BAD_PRACTICE_4
:
In function '...':
error: '...' was not declared in this scope
...
Compiler-specific extensions
Probably each compiler has some intentional extensions to the C++-standard that can be toggled via compiler switches [7]. When creating a C++ project in VS2008, these extensions are automatically turned on and can be toggled via the project settings > C/C++ > "Language" > "Disable language extensions". Changes may not be restricted to documented features, but may affect other behavior as well.
The following sample class template provides a typedef
that is referenced in the sub-class template. typedef
declarations are normally not visible in sub-classes. The name of this beast is Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class? [8]
namespace example5
{
template<typename t>
class base {
public:
typedef t value_type;
};
template<typename t>
class derived : public base<t> {
#ifdef BAD_PRACTICE_5
value_type my_int;
#else
typename base<t>::value_type my_int;
#endif
};
}
GCC's reaction to this is:
error: '...' does not name a type
...
Looking at [8], you might wonder if VC++ 2008 would accept the following:
#ifdef BAD_PRACTICE_5
base<t>::value_type my_int;
#else
Have a guess ;)
References
I intentionally don't give links here. Links may change. Google search results adapt.
- open-std.org: Definition of Dependent Name - Revision 2
- IBM XL C/C++ V8.0 for AIX: Name binding and dependent names (C++ only)
- MSDN: Templates and Name Resolution
- Standard for Programming Language C++
- MSDN: Visual C++ .NET 2003 Summary of Compile-Time Breaking Changes
- StackOverflow: Visual C Compiler allows Dependent Name as a Type Without Typename
- MSDN: Microsoft Extensions to C and C++
- Parashift.com C++ FAQ Lite: [35.18] Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?
History
- 2011/07/18: Original version.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.