This code does not compile:
template <typename T>
struct B {
typedef T type;
};
struct D0 : public B<int> {
void h(type) { }
};
template <typename T>
struct D : public B<T> {
void f(typename B<T>::type) { }
void g(type) { }
};
http://ideone.com/pVh94t
Specifically, although D0::h(type) and D::f(typename B<T>::type) compile, D::g(type) does not. Why is type not visible in D?
Answer can be found in two places. First C++11 draft standard N3337:
§14.6.2/3
In the definition of a class template or a member of a class template,
if a base class of the class template depends on a template-parameter,
the base class scope is not examined during unqualified name lookup
either at the point of definition of the class template or member or
during an instantiation of the class template or member. [ Example:
typedef double A;
template<class T> class B {
typedef int A;
};
template<class T> struct X : B<T> {
A a; // a has type double
};
The type name A in the definition of X<T> binds to the typedef
name defined in the global namespace scope, not to the typedef name
defined in the base class B<T>. — end example ]
(Bold emphasis mine)
Second, the parashift FAQ provides a human-readable explanation in Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?:
Perhaps surprisingly, the following code is not valid C++, even though
some compilers accept it:
template<typename T>
class B {
public:
class Xyz { /*...*/ }; // Type nested in class B<T>
typedef int Pqr; // Type nested in class B<T>
};
template<typename T>
class D : public B<T> {
public:
void g()
{
Xyz x; // Bad (even though some compilers erroneously (temporarily?) accept it)
Pqr y; // Bad (even though some compilers erroneously (temporarily?) accept it)
}
};
This might hurt your head; better if you sit down.
Within D<T>::g(), name Xyz and Pqr do not depend on template parameter
T, so they are known as a nondependent names. On the other hand, B<T>
is dependent on template parameter T so B<T> is called a dependent
name.
Here’s the rule: the compiler does not look in dependent base classes
(like B<T>) when looking up nondependent names (like Xyz or Pqr). As a
result, the compiler does not know they even exist let alone are
types.
At this point, programmers sometimes prefix them with B<T>::, such as:
template<typename T>
class D : public B<T> {
public:
void g()
{
B<T>::Xyz x; // Bad (even though some compilers erroneously (temporarily?) accept it)
B<T>::Pqr y; // Bad (even though some compilers erroneously (temporarily?) accept it)
}
};
Unfortunately this doesn’t work either because those names (are you
ready? are you sitting down?) are not necessarily types. “Huh?!?” you
say. “Not types?!?” you exclaim. “That’s crazy; any fool can SEE they
are types; just look!!!” you protest. Sorry, the fact is that they
might not be types. The reason is that there can be a specialization
of B<T>, say B<Foo>, where B<Foo>::Xyz is a data member, for example.
Because of this potential specialization, the compiler cannot assume
that B<T>::Xyz is a type until it knows T. The solution is to give the
compiler a hint via the typename keyword:
template<typename T>
class D : public B<T> {
public:
void g()
{
typename B<T>::Xyz x; // Good
typename B<T>::Pqr y; // Good
}
};
Apparently, this is due to the compiler's order of semantic compilation phases. It happens that, to the compiler's view, the symbol type is not present in any scope, because it hasn't (yet) instantiated templates, and thus, it hasn't instantiated D itself.
The only workaround is to force the compiler to resolve the symbol in a later phase of semantic compilation, such as with...
void f(typename B<T>::type) {}
Or, if the derived class is not a template, but the base class is, the compiler is forced to resolve/instantiate/whatever the base class first, thus avoiding any possible errors inside the derived class.
But this is not the case if both base and derived are templates, because the compiler will look-up a symbol for the derived in all already instantiated scopes (such as the global scope) before instantiating the derived template class, and if it fails, well you already know what happens with int main(){printf("%d\n", argc);}, no?
Related
I have some code like this
class A : public b<T>
{
public:
typedef b<T> _baseclass; // why we need this declaration
};
What is the use of typedef inside the class?
Is the definition limited to this class only?
Shall we create this as static and use without crating an object of the class?
This member type will be available outside of the class definition too, which is convenient in template code. If you passed an A into a function template, or maybe some other classes that also have _baseclass member types, then you can use _baseclass to find out what the base is without needing to know exactly what the top-level type is.
Standard templates like std::vector and std::map have member types like value_type — these do not signify a base class but have a similar purpose, in that you can use value_type anywhere a container is used, no matter which container is used.
Swapping typedef to using (because I want to), here's an example:
// The class templates
template <typename T>
struct Base {};
struct A : Base<int>
{
using base_class = Base<int>;
};
struct B : Base<char>
{
using base_class = Base<char>;
};
struct C : Base<bool>
{
using base_class = Base<bool>;
};
// The example
template <typename T>
void foo()
{
// typename needed because base_class is a "dependent name"
// (just go with it)
typename T::base_class the_base;
// This line is to suppress "unused variable" warnings
(void)the_base;
}
int main()
{
foo<A>();
foo<B>();
foo<C>();
}
Though this particular program doesn't actually "do anything", it shows a function template foo that can "know" what the base class was in each case, without any further information about exactly what T is. And it'll work for any class to which you've added a base_class member type!
Consider the following example:
template <typename T>
class A {
private:
typedef typename T::C C;
};
template <typename T>
class B : public A<B<T>> {
public:
typedef T C;
};
int main() {
B<int> b;
}
Compiling it with GCC gives the following error:
test.cc:5:23: error: no type named 'C' in 'B<int>'
typedef typename T::C C;
~~~~~~~~~~~~^
test.cc:9:18: note: in instantiation of template class 'A<B<int> >' requested here
class B : public A<B<T>> {
^
test.cc:15:10: note: in instantiation of template class 'B<int>' requested here
B<int> b;
^
Why does compiler give an error if B::C is defined and how to fix it?
At this point,
class B : public A<B<T>> {
… class B is incomplete. Class A can't look inside it.
The C type definition inside B is accessible from that point inside B, and on. It's also available inside function bodies in B because you can regard a function definition inside the class definition as a shorthand for placing it after the class. But an incomplete class contains nothing as viewed from outside: all that outside code can do is form pointers and references and use the class as template argument.
template< class C >
using Ungood = typename C::Number;
struct S
{
void foo() { Number x; (void) x; } // OK
Ungood<S> uhuh; //! Nyet.
using Number = double;
};
auto main() -> int {}
You can fix your code by changing the design. The most obvious is to pass the type as a separate template argument. But depending on what you're trying to achieve it may be that the inheritance you currently have, isn't really needed or even useful.
You can't because you're in a chicken-egg paradox. The definition of the base requires knowledge of the definition of the derived, which needs the definition of the base to complete. You simply have to come up with an alternative. One example would be to use an external metafunction to communicate the needed type to whoever needs it. Hopefully that's not in any part of the definition of the base's members or you're probably screwed.
Other alternative is to pass T as a second parameter.
You can't do that because of this:
A class is considered defined after the closing brace of its class-specifier has been seen [...]
And a few exceptions, none of which are valid in your case.
In other terms, you must consider your derived class as not fully defined when you try to use it in your base class to access the type C.
Anyway, you can exploit the fact that your derived class is a template class and do this:
template <typename T>
class A;
template <template<typename> class D, typename T>
class A<D<T>> {
private:
using C = T;
};
Aa you can see, I've not given a definition for the primary template class, thus only the specialization for template classes can be used.
Not sure this is the OP's real case, but it's the case in the example in the question.
My question is, why does the following code not compile:
template<typename t> class c1
{
public:
typedef int type_name;
void fn1(type_name x) {}
};
template<typename t> class c2 : public c1<t>
{
public:
void fn2(type_name x) {}
};
While the following does:
class c1
{
public:
typedef int type_name;
void fn1(type_name x) {}
};
class c2 : public c1
{
public:
void fn2(type_name x) {}
};
As you see, the only difference is that in the first case the classes are templates. Gcc and Clang complain that type_name is not defined in the second class (only in template version). Are typedefs not inherited from parent class? If so, why does it work on non-template version? Is there some exception when using typedefs from templates?
Also, I know that I can make this work with fully-qualified type name, i.e. 'typename c1::type_name'. I just want to know if this is some C++ restriction or compiler bug.
They are actually "inherited" (by which I mean they are accessible as members of c2<t>). However, all members (not just types) inherited from a dependent base class (a base class which depends on template parameters) are only looked up inside the class template when the compiler has reason to believe they're dependent.
In other words, when parsing the definition of the template c2, the compiler doesn't know what t will be when instantiating, and so it has no means of guessing what the definition of c1<t> will be (remember that it could be specialised later). So it does not look into c1 at all when looking up a name found in c2. So type_name is not found.
However, if you make the name lookup explicitly depend on template parameters somehow, the compiler will realise it has to postpone the lookup until instantiation. This would happen in these cases:
// Case 1
template<typename t> class c2 : public c1<t>
{
public:
void fn2(typename c1<t>::type_name x) {}
};
// Case 2
template<typename t> class c2 : public c1<t>
{
public:
void fn2(typename c2::type_name x) {}
};
In both cases, the thing to the left of :: depends on template parameters, so the compiler will know to postpone lookup until instantiation.
Also note that you need to add the typename keyword; without it, the compiler would have no idea whether the thing to the right of :: is a type or a non-type member. The language standard dictates to treat it as a non-type member in such case, which leads to a syntax error.
However, when c2 is not a template, the definition of all its base classes is fully known when it's parsed, so there is no problem in looking up the name.
You can learn more details about this two-phase lookup in this somewhat related SO question.
I have a beginner question regarding nested classes in c++. There are a lot of information on the internet about this but i still can't get it to work properly without compile errors. I simply want to return the inner class B from a function inside class A.
template<class T>
class A
{
public:
A();
class B
{
public:
B(){};
};
B getB();
};
template<class T>
A<T>::A(){}
template<class T>
B A<T>::getB{return B();}
When i do this i get the error "B does not name a type". I believe the solution is to use typedef class A<T>::B b_class;, but i still end up with the same error.
Thank you!
You need to change a few things: add parens to the function definition, qualify the outer B with the enclosing class (A<T>::) alongside with typename (required because the type depends on the type of A) and change the return to actual object creation.
Here, listed in fixed source:
template<class T>
typename A<T>::B A<T>::getB() {return B();}
//^----------------------------------------------typename
// ^---------------------------------------qualification
// ^---------------------parens
// ^---------c-tor call
template<class T>
B A<T>::getB{return B b;}
There are three issues here:
B at the start of the declaration does not name the type A<T>::B. You need to be explicit about it, either by using typename A<T>::B or with a trailing return type so that B refers to the correct name.
You don't have () after getB
Your syntax for creating a temporary B is incorrect, it should be B() or B{}
Here is a fixed version:
template<class T>
auto A<T>::getB() -> B { return B{}; }
A is the name of a template, so A<T> is the name of the (instantiated) class.
Thus, the full name of the nested class is A<T>::B, but since B depends on the template parameter T and is a type, you must specify template A<T>::B.
The tricky bits is that inside the declaration of A you can refer to B simply by using the B name. And you can refer to the instantiation of A<T> by using A, using what is called the injected class name. So inside the class declaration, A can refer both to the template and the class instantiation.
Answering your question:
template<class T>
typename A<T>::B A<T>::getB()
{
B b;
return b;
}
Note that the return type is outside the declaration of the function, so you need to use the fully qualified name.
I caught myself "inventing" this simple construct lately when working with many templated classes and deriving from them. I am not sure if it is common practice, or am I tying a rope around my neck.
template <typename T> class Base {};
template <typename T> class Derived : public Base<T>{
typedef Base<T> Base;
};
I found it especially useful if the Base class has its own typedefs for some types. E.g:
template <typename T> class Base {
typedef T Scalar;
typedef Matrix<Scalar> Matrix;
};
Then it's easy to "import" types into the Derived. It saves re-typing the template signature. E.g:
template <typename T> class Derived : public Base<T>{
typename Base<T>::Matrix yuck_yuck(); //that's what I am trying to simplify
typedef typename Base<T> Base;
typedef typename Base::Matrix Matrix;
Matrix much_fun(); //looks way better
};
Also on of the big advantages is that, when you want to add another template parameter to the Base class. You don't have to go over a bunch of functions to change, just update the typedef. much_fun will have no problem if Base will be changed to Base<T,U> while yuck_yuck will need to have updated signatures (not sure if template parameter is formally included with the signature, so pardon me if I am making a formal error here, but I think it is).
Is this a good practice or am I playing with a gun next to my vital parts? It looks like it makes code more readable, and simplifies it, but maybe I am missing something that can backfire.
EDIT2: I got the working example. The Base class must be within its namespace or there will be conflicts with the same names within a scope, as the commenters pointed out. Below is the minimal example that embodies my real question.
namespace Fun {
template <typename T> class Base {
public:
typedef T Scalar;
};
}
template <typename T>
class Derived : public Fun::Base<T>{
public:
typedef typename Fun::Base<T> Base;
typedef typename Base::Scalar Scalar;
typename Fun::Base<T>::Scalar yuck_yuck();
Scalar much_fun();
};
#include <iostream>
using namespace std;
int main() {
Derived<double> d;
return 0;
}
With lots of stuff the code gets really bloated with typenames, and template parameters. But I already run into a trouble making up the example, by not placing Base in its own namespace. I wonder if there are any other caveats, that are actually killers to the idea.
I believe this is ill-formed, due to rule 2 of C++11 3.3.7/1
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.
meaning that you can't use the name Base to refer to both the template and the typedef within the class scope. Certainly, my compiler won't accept it:
error: declaration of ‘typedef struct Base<T> Derived<T>::Base’ [-fpermissive]
error: changes meaning of ‘Base’ from ‘struct Base<T>’ [-fpermissive]
(NOTE: this refers to the simplified example originally posted, and doesn't cover the updated question where the base class name is in a different scope.)
I actually consider it a helpful (and good) practice if the typedef is not exposed public or protected:
// No template, not Base, to avoid that discussion
class Derive : public SomeBaseClass
{
private:
typedef SomeBaseClass Base;
public:
typedef Base::T T;
T f();
};
class MoreDerived : public Derived
{
// Base is not accessible
};