Templates :Name resolution -->IS this statement is true while inheritance? - c++

This is the statement from ISO C++ Standard 14.6/6:
Within the definition of a class template or within the definition of a member of a class template, the keyword typename is not required when referring to the unqualified name of a previously declared member of the class template that declares a type. The keyword typename shall always be specified when the member is referred to using a qualified name, even if the qualifier is simply the class template name. [Example:
template<class T> struct A {
typedef int B;
A::B b; // ill-formed: typename required before A::B
void f(A<T>::B); // ill-formed: typename required before A<T>::B
typename A::B g(); // OK
};
The keyword typename is required whether the qualified name is A or A<T> because A or A<T> are synonyms within a class template with the parameter list <T>. ]
Is this statement is true while inheritance?
If yes, can anyone explain this?
I checked with inner class; it is accepted? But I am unable to check with inheritance?

Yes, that is equally true of inherited members.
The keyword typename is required for members of base templates, but not base classes in general. The reason it is required for base templates is that their members are not automatically brought into the scope of the class {} block, so the only way to refer to them is with a qualified-id, which requires typename.
template< typename >
class base1
{ typedef int type1; };
class base2
{ typedef int type2; };
template< typename A >
class derived
: base1< A >, base2 {
typename base1< A >::type1 x;
type2 y;
};

Related

Injected class name as type

Given the following code,
template <class> using void_t = void;
template <class C, class = void> struct X { enum { v = 0 }; };
template <class C> struct X<C, void_t<typename C::T> > { enum { v = 1 }; };
struct T { };
int main() { return X<T>::v; }
what should main return? GCC and MSVC say 1, Clang says 0.
I think Clang is right here. The rule in [class.qual] is:
In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C:
if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C ([class]), or
[... irrelevant here ...]
the name is instead considered to name the constructor of class C. [ Note: For example, the constructor is not an acceptable lookup result in an elaborated-type-specifier so the constructor would not be used in place of the injected-class-name. — end note ] Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor or in a using-declaration. [ Example:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba; // object of type A
A::A a; // error, A​::​A is not a type name
struct A::A a2; // object of type A
— end example ]
typename C::T is the same kind of thing as A::A, it's lookup in which function names are not ignored (typename doesn't cause function names to be ignored). So, in typename C::T, when C is T, the name T is considered to name the constructor. As it's not a type name, we should get a substitution failure and fallback to the primary template.
Filed 86818.
To complete Barry's answer, typename only says to the compiler that the following name is a type for the analysis performed before template instatiation. After instantiation, name look up is performed as if typename was not there, [temp.res]/4:
The usual qualified name lookup is used to find the qualified-id even in the presence of typename.
So Clang is right. In order to get a consistent compiler behaviour you can use the elaborated type specifier struct C::T in place of typename C::T:
template <class> using void_t = void;
template <class C, class = void> struct X { enum { v = 0 }; };
template <class C> struct X<C, void_t<struct C::T> > { enum { v = 1 }; };
struct T { };
int main() { return X<T>::v; }

typedef and template parameter with same name

Why is that case incorrect (it's logical)
template <typename T>
struct Der: public Base
{
typedef int T;
T val;
};
, but that case is correct?
struct Base
{
typedef int T;
};
template <typename T>
struct Der: public Base
{
T val;
};
The Standard 14.6.1/7 says:
In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, for each base class which does not depend on a template-parameter (14.6.2), if the name of the base class or the name of a member of the base class is the same as the name of a template-parameter, the base class name or member name hides the template-parameter name (3.3.7).
Why is it not ambiguous in here?
The first example is incorrect according to [temp.local]/6:
A template-parameter shall not be redeclared within its scope (including nested scopes).
However, in
template <typename T>
struct Der: public Base
{
T val;
};
T is hidden by the name inherited from Base - as specified by your quote.
[..] if the name of the base class or the name of a member of
the base class is the same as the name of a template-parameter, the
base class name or member name hides the template-parameter name
(3.3.7).
That is, the member val is of type int. Demo.
In general, the standard tries to ensure that the meaning of a type in
a given scope is the same.
If we pretend that the typedef is allowed then consider the types of val1, val2, and val3 in the following?
template <typename T>
struct Der: public Base
{
void f1() {
T val1; // What is the type of 'val1'?
}
T val2; // What is the type of 'val2'?
typedef int T;
T val3; // What is the type of 'val3'?
};
The only variable to have the template parameter type T would be
'val2'.
This is because the standard requires that all members of the class be "in scope" for f1. This is why member functions can refer to member variables defined later in the class body.
The same problem can be demonstrated with typedefs too:
typedef int T;
struct S
{
T x;
typedef float T;
T y;
};
Again, if this was legal then the T used for the declaration of x would refer to ::T and the T used for y would refer to typedef S::T.
This is covered by the standard under ISO 3.3.7/1:
The following rules describe the scope of names declared in classes.
...
2) A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.
Because the second is unambigous:
You might not care or know that there was the typedev int T in the superclass, but you've just introduced T as template parameter, which makes it clear that you're referring to it when using T in Der.

Should a class-member using-declaration with a dependent qualified-id be a dependent name?

Draft N3337 of the C++11 standard states in [namespace.udecl]
A using-declaration introduces a name into the declarative region in which the using-declaration appears.
Every using-declaration is a declaration and a member-declaration and so can be used in a class definition.
In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the
class being defined.
This is generally used to make a protected typedef within a base-class public in the derived class, as in the following example, which compiles successfully in the latest version of Clang:
struct A
{
protected:
typedef int Type;
};
struct B : A
{
using A::Type;
};
B::Type x;
The using-declaration can refer to a template class. This compiles:
struct A
{
protected:
template<typename T>
struct Type
{
};
};
struct B : A
{
using A::Type;
};
B::Type<int> x;
It's also possible to refer to a template in a dependent base-class. The following compiles successfully (with the typedef commented.)
template<typename T>
struct A
{
protected:
template<typename U>
struct Type
{
};
};
template<typename T>
struct B : A<T>
{
using /* typename */ A<T>::Type; // A<T> is dependent, typename required?
// typedef Type<int> IntType; // error: unknown type name 'Type'
};
B<int>::Type<int> x;
Uncommenting the typename causes an error when instantiating B<int>: "error: 'typename' keyword used on a non-type".
Uncommenting the typedef causes an error when parsing B before its first instantiation. I'm guessing this is because the compiler does not treat Type as a dependent type-name.
The last paragraph of [namespace.udecl] suggests that using-declarations may specify dependent names, and that the typename keyword must be used in order to disambiguate further usage of the name introduced:
If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2), the name introduced
by the using-declaration is treated as a typedef-name
My reading of [temp.dep] suggests that A<T>::Type is a dependent name. It follows logically that the name introduced by the using-declaration should also be dependent, but [temp.dep] does not explicitly mention the case of a dependent using-declaration. Am I missing something?
The problem is that Type is not a class, but a class template. You can do the following (this way you tell the compiler that Type is a class template in scope of B):
template<typename T>
struct B : A<T>
{
using A<T>::Type;
typedef typename B::template Type<int> IntType;
};
Actually, in your second example in order to write typedef for IntType you'd have to do the same.
Yes, a class-member using-declaration with a dependent qualified-id introduces a dependent name.
[namespace.udecl]
A using-declaration introduces a name into the declarative region in which the using-declaration appears.
If the name introduced is dependent, it stays dependent - I can find nothing to suggest otherwise.
However, the using-declaration syntax does not provide a way to specify that a dependent name is a template. Subsequent references to the dependent name Type within B may or may not refer to a template, so Type<int> cannot be parsed.
The following example demonstrates valid usage of a dependent using-declaration.
template<typename T>
struct A
{
protected:
struct Type
{
typedef int M;
};
};
template<typename T>
struct B : A<T>
{
using typename A<T>::Type;
typedef typename Type::M Type2;
};
B<int>::Type2 x;

Using typedefs of a non-template derived class in the base class when using CRTP

I'm using CRT pattern and want the base class to see typedefs from the derived class. In this post #James McNellis suggested to do that using base_traits class and it works fine. But in the case described in that post the derived class itself is a template. This approach does not work in VS2010 when the derived class is not a template.
template <class D>
struct base_traits;
template <class D>
struct base
{
typedef typename base_traits<D>::value_t value_t;
};
struct derived : base<derived>
{
typedef typename base_traits<derived>::value_t value_t;
};
template<>
struct base_traits<derived>
{
typedef int value_t;
};
The above code gives lots of errors. The first one is:
error C2027: use of undefined type 'base_traits
on the line of the base class's typedef.
base_traits<derived> must be declared and defined prior it's usage since it is needed for the implicit instancation of base<derived> (below, I forward declared derived) :
template <class D>
struct base_traits;
template <class D>
struct base
{
typedef typename base_traits<D>::value_t value_t;
};
struct derived;
template<>
struct base_traits<derived>
{
typedef int value_t;
};
struct derived : base<derived>
{
typedef base_traits<derived>::value_t value_t;
};
int main(void)
{
derived d;
}
Live demo
§14.7.3 [temp.expl.spec]/p7:
The placement of explicit specialization declarations for function
templates, class templates, variable templates, member functions of
class templates, static data members of class templates, member
classes of class templates, member enumerations of class templates,
member class templates of class templates, member function templates
of class templates, static data member templates of class templates,
member functions of member templates of class templates, member
functions of member templates of non-template classes, static data
member templates of non-template classes, member function templates of
member classes of class templates, etc., and the placement of partial
specialization declarations of class templates, variable templates,
member class templates of non-template classes, static data member
templates of non-template classes, member class templates of class
templates, etc., can affect whether a program is well-formed according
to the relative positioning of the explicit specialization
declarations and their points of instantiation in the translation unit
as specified above and below. When writing a specialization, be
careful about its location; or to make it compile will be such a trial
as to kindle its self-immolation.
In particular (§14.7.3 [temp.expl.spec]/p6),
If a template [...] is explicitly specialized then that specialization
shall be declared before the first use of that specialization that
would cause an implicit instantiation to take place, in every
translation unit in which such a use occurs; no diagnostic is
required. If the program does not provide a definition for an
explicit specialization and [...] the specialization is used in a way
that would cause an implicit instantiation to take place [...],
the program is ill-formed, no diagnostic required.
The explicit specialization base_traits<derived> must be declared and defined before the definition of derived, as otherwise both inheriting from base<derived> and using base_traits<derived>::value_t would trigger an implicit instantiation. Thus:
template <class D>
struct base_traits;
template <class D>
struct base
{
typedef typename base_traits<D>::value_t value_t;
};
struct derived;
template<>
struct base_traits<derived>
{
typedef int value_t;
};
struct derived : base<derived>
{
typedef base_traits<derived>::value_t value_t;
};

How do I use the class-name inside the class itself as a template argument?

I've got a templated class having two template paramters
template <class T, class U> class A /* ... */
and another template class that accepts a template class with two arguments as template parameter.
template <class T, class U, template<class X, class Y> class Z>
class B
{
typedef typename Z<T,U>::pointer pointer;
};
Is it impossible to create an instance of B in A where Z is A?
template <class T, class U>
class A
{
public:
B<T,U,A> foo (void) // compiler complaining here
{
B<T,U,A> test; // and here
return test;
}
};
A free function doing the same thing isn't problematic at all.
template<class T, class U>
B<T, U, A> bar (void)
{
B<T,U,A> test;
return test;
}
In other words: Is there any rule I didn't fell over yet that prevents me from using the name of the class I am in as a template argument?
The code is:
template <class T, class U, template<class X, class Y> class Z>
class B
{
typedef typename Z<T,U>::pointer pointer;
};
template <class T, class U>
class A
{
public:
B<T,U, A> foo (void)
{
B<T,U,A> test;
return test;
}
};
template<class T, class U>
B<T, U, A> bar (void)
{
B<T,U,A> test;
return test;
}
int main (void)
{
return 0;
}
And the MSVC 2012 compiler gives a Compiler Error 3200.
'A<T,U>' : invalid template argument for template parameter 'Z', expected a class template
Your compiler, MSVC, seems to follow the general rule laid down in §14.6.2.1/1 C++11:
A name refers to the current instantiation if it is, [...] in the definition of a class template, [...] the injected-class name [...] of the class template [...]
Inside the definition of class template A, the name A can be used because it is "injected" into the local (class) scope of A. Therefore, and famously, you can use A as well as A::A, as well as A::A::A and so on, to refer to A. By the rule quoted above, all of these expressions refer to the current instantiation (i.e. a specific type like A<int,float>), and not to the name of template A itself.
However, there is another rule, in §14.6.1/1:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used [...] as a template-argument for a template template-parameter [...] it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.
(Note that in C++03, there is no such exception for template template parameters; 14.6.1/1, in fact, is worded entirely differently. Under C++03, MSVC's interpretation of the rules was probably correct.)
Given the C++11 rule, however, in the member declaration
B<T,U,A> test;
inside the definition of A, the name A is clearly used as an argument for a template template parameter and therefore must be interpreted as template name, not as type name referring to the current instantiation.
However, it is not uncommon that compilers are confused in situations like this. There are two valid ways to tell them how to interpret A:
Using the so-called normal name ::A rather than the injected one:
B<T,U,::A> test;
This is possible because of §14.6.1/5 (which was 14.6.1/2c in C++03):
When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used, it always refers to the class template itself and not a specialization of the template. [...]
Using the injected one explicitly, but designating it as a template:
B<T,U,A::template A> test;
Both methods have been confirmed as solving this problem in MSVC.
If class A is defined before class B, I'm getting the error: 'B' does not name a type (in other words, B is not defined yet). Is this the error you are getting? It can be fixed either by placing B in front of A, or, if the two are referencing each other, by forward-declaring class B before A like this:
template <typename T, class U, template<typename X, class Y> class Z> class B;