I have a c++ template class that has a nested class inside, something like:
template<int d>
class Outer_t
{
public:
class Inner;
Inner i;
};
template<int d>
class Outer_t<d>::Inner
{
public:
float x;
};
int main ()
{
Outer_t<3> o_t; // 3 or any arbitrary int
o_t.i.x = 1.0;
return 0;
}
This compiles without any problems. However, as soon as I declare a similar non-template class, something like this:
class Outer_1
{
public:
class Inner;
Inner i;
};
class Outer_1::Inner
{
public:
float x;
};
int main ()
{
Outer_1 o1;
o1.i.x = 1.0;
return 0;
}
I start getting the following error (I'm using gcc 4.6.3): "error: field ‘i’ has incomplete type". I know that I can remedy this by simply defining the inner class inline, inside the outer class:
class Outer_2
{
public:
class Inner {
public:
float x;
};
Inner i;
};
This will compile, but I would like to avoid defining the nested class inline. So I have two questions: what is the reason for this apparently odd discrepancy between a template and non-template nested class declaration? And is there an elegant way to declare and use the nester class inside the outer class, while avoiding defining it inline, much in the same style as the template class? Thanks in advance for your help!
Why the compilation error?
The compiler needs to know the size of the member variable types in order to do object layout. For Outer_1, because Inner is simply a forward declaration, we don't know its size. Hence the compilation error. For Outer_2 however, the definition is inline, so we know the size of it by the time we get to the member variable of type Inner.
Why do templates get a pass?
Class templates, on the other hand, don't get defined until template instantiation occurs. In your example, the implicit instantiation occurs in main. At that point, the definition of Inner is complete and therefore its size is known. We can see that this is the case by using explicit instantiation before the definition of Inner.
template<int d>
class Outer_t
{
public:
class Inner;
Inner i;
};
template class Outer_t<3>; // explicit instantation
template<int d>
class Outer_t<d>::Inner
{
public:
float x;
};
int main ()
{
Outer_t<3> o_t; // 3 or any arbitrary int
o_t.i.x = 1.0;
return 0;
}
Clang produces the following error:
a.cc:7:11: error: implicit instantiation of undefined member 'Outer_t<3>::Inner'
Inner i;
^
a.cc:10:16: note: in instantiation of template class 'Outer_t<3>' requested here
template class Outer_t<3>;
^
a.cc:5:11: note: member is declared here
class Inner;
^
The Solution
If you want to pull out the definition of the nested class, my suggested solution is to have it templatized just like Outer_t, but provide an alias for your convenience.
template <typename Dummy = void>
class Outer_1_Impl {
public:
class Inner;
Inner i;
};
template <typename Dummy>
class Outer_1_Impl<Dummy>::Inner {
public:
float x;
};
using Outer_1 = Outer_1_Impl<>;
int main () {
Outer_1 o1;
o1.i.x = 1.0;
}
You can only have non-static members in an object which have a complete definition when defining the class. In your non-template example, Outer_1::Inner is clearly not complete as it is only declared so far. The only way to create a member of Inner is, indeed, to define it like it is done for Outer_2.
Now, why does the problem not arise when using a class template? The answer is: when you are instantiating Outer_t in main(), there actually is a complete definition of Outer_t<T>::Inner accessible! ... and before instantiating Outer_t<T> with some type T the definition of Outer_t isn't really needed.
Related
I am new to C++. During my learning phase I encountered with below issue.
I am trying to derive a class stack from a class template Queue.
Compiler throws below error in constructor of stack
..\src\TrainingDay2.cpp:44:3: error: 'b' was not declared in this scope
b=a;
Please help to find out the root cause.
#include <iostream>
using std::cout;
using std::endl;
template<class T> class Queue //Base class
{
private:
T ArrQueue[20];
protected:
T* a;
T* b;
public:
Queue() { cout<<"QUEUE CONST "<< endl; }
void push(T x);
void pop(void);
};
template <class T>
class stack :public Queue<T> // Derived class
{
public:
stack():Queue<T>() {
b=a;
}
void pop() {
b--;
}
};
int main()
{
stack<int> S;
return 0;
}
Because the base class is a template, whose instantiation depends on a template parameter of the derived class, and you're trying to name a member of the base class, two-phase lookup mandates that you write this->b, not b.
(And that default constructor call is not needed.)
stack()
{
this->b = this->a;
}
void pop()
{
this->b--;
}
(live demo)
Welcome to C++… :P
[C++11: 14.6.2/3]: In the definition of a class or class template, if a base class 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. [..]
The reason is that inside a template, two-phase name lookup rules apply:
Names which do not depend on template parameters are looked up (=resolved) when the template is defined (=parsed).
Names which do depend on template parameters are looked up when the template is instantiated (when you provide the template arguments).
Base classes which depend on template parameters are only searched during name lookup at instantiation time.
The reason for this two-phase lookup is that until the template arguments are known, the compiler cannot know what the definition of the base class (or other dependent construct) will be. Remember that template specialisation exists.
You need to somehow tell the compiler that the name b is dependent. You basically have three ways to do that:
Prefix the name with this->; since you're in a class template, all your members depend on template parameters implicitly:
this->b = a;
Use full qualification for the name. This will make the dependency on template parameters explicit:
Queue<T>::b = a;
Put a using declaration into your class to inform the compiler that the name comes from a dependent base class:
template <class T>
class stack :public Queue<T> // Derived class
{
protected:
using Queue<T>::b;
public:
stack():Queue<T>()
{
b=a;
}
void pop()
{
b--;
}
};
In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,
You must make the names dependent, e.g. by prefixing them with this->.
gcc-problem-using-a-member-of-a-base-class-that-depends-on-a-template-argument
And yes, it can be valid in VS.
In the following program "Here" is printed:
#include <iostream>
class Base
{
static bool temp;
static bool initTemp()
{std::cout<<"Here\n";return true;}
};
bool Base::temp = Base::initTemp();
class Derived : public Base
{};
int main() {int a;std::cin>>a;}
In the following program "Here" is not printed:
#include <iostream>
template <class T>
class Base
{
static bool temp;
static bool initTemp()
{std::cout<<"Here\n";return true;}
};
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
class Derived : public Base<int>
{};
int main() {int a;std::cin>>a;}
In both cases Base is never referenced. The only difference is that in the second case it is a template class. Can anyone explain to me why this behavior occurs. I am using VS 2012.
In both cases Base is never referenced.
And that is precisely the reason why you see nothing being printed to the standard output.
The definition of a class template's static data member does not get instantiated unless you use that data member; like member functions, the definitions of static data members of a class template are instantiated on demand.
This is specified in paragraph 14.7.1/1 of the C++11 Standard:
[...] The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions,
member classes, scoped member enumerations, static data members and member templates. [...]
Since your client code never refers to Base<>::temp, there is no need to construct and initialize it.
As a side note, this signature:
void main()
Is not valid (standard) C++. If you want to write portable code, the return type of main() should always be int.
In the first case, you don't instantiate Base, but you do call the static function:
bool Base::temp = Base::initTemp();
In the second case, you never instantiate the template:
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
You can explicitly instantiate the Base class template, as with:
template class Base<int>;
And then you will see "Here" printed.
You cannot create variable of undefined class, which you seem to do with line:
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
You cannot allocate variable of undefined type. What you need is write something like:
Base<int>::temp = value;
of cause there will be different variable allocated for each type provided, so you cannot have common static variable for a template class. You'll have separate variable for each type you instantiate your template instead.
To downvoters here is complete example:
#include <iostream>
template<class T>
class X
{
public:
static int v;
};
template<class T>
int X<T>::v = 0;
int main()
{
X<int>::v = 3;
X<char>::v = 2;
using namespace std;
cout << X<char>::v << endl << X<int>::v;
}
It prints 2 3 which means you cannot have single variable for all classes you'll instantiate your template.
I am making a template class with an inner utility class. All specializations of the template want the same inner class:
template<...> class Outer {
class Inner { };
};
That gives me Outer<...>::Inner but I want all Inner to be the same type, as if I'd just written:
class Inner { };
template <...> class Outer { };
or if Outer were simply not a template class:
class Outer {
class Inner { };
};
giving me Outer::Inner. I'd like to have Outer::Inner work for all Outer<> if that's possible (just for namespace/clarity reasons). Otherwise of course I can just move Inner out.
The nested class can be a non-template, but every instantiation of the template will have its own nested class because they're (otherwise) unrelated types. You can do
namespace detail {
class Inner {};
} // detail
template<...>
class Outer {
typedef detail::Inner Inner;
};
The way I've done this in the past is using inheritance:
class DummyBase{
protected:
class Inner{
//etc...
};
};
template<...> class Outer : public DummyBase{
//etc...
};
It will be unique for each instantiation of Outer. I.e.,
Outer<int>::Inner will be a different type from Outer<double>::Inner
I understand that the member names of a base-class template are hidden within the scope of a derived class, and therefore must be accessed using this->foo or Base<T>::foo. However, I recall that C++ also allows you to use the using keyword, which can come in handy in a derived-class function which frequently accesses a base-class variable. So, in order to avoid cluttering up the function with this-> everywhere, I'd like to use the using keyword.
I know I've done this before, but for whatever reason I can't get it to work now. I'm probably just doing something stupid, but the following code won't compile:
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
void dosomething()
{
using Base<T>::x; // gives compiler error
x = 0;
}
};
int main()
{
Derived<int> d;
}
The error, (with GCC 4.3) is: error: ‘Base<T>’ is not a namespace
Why doesn't this work?
It doesn't work because C++ language has no such feature and never had. A using-declaration for a class member must be a member declaration. This means that you can only use in class scope, but never in local scope. This all has absolutely nothing to do with templates.
In other words, you can place your using-declaration into class scope
struct Derived : public Base<T> {
...
using Base<T>::x;
...
};
but you can't have it inside a function.
Using-declarations for namespace members can be placed in local scope, but using-declarations for class members cannot be. This is why the error message complains about Base<T> not being a namespace.
Outside class scope (if you are in a block etc), you can only name namespace members in a using declaration.
If you don't want to place that using declaration into the scope of Derived (which IMO is the favorable solution), your other option is to use a reference
int &x = this->x;
x = 0;
It should be noted that this is semantically different, because it
Forces a definition to exist for Base<T>::x, which might not be the case for static const data members
Forces Base<T>::x to be of type int& or be convertible to type int&.
Otherwise, if you want to avoid using this-> all again, I don't see other options.
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
using Base<T>::x;
void dosomething()
{
x = 0;
}
};
int main()
{
Derived<int> d;
}
As others said, it is working only class scope.
I can't get this to compile at all. I may not be possible but I don't know why it should not be.
class A {
template <typename T>
class B {
int test() { return 0; }
};
//template <> class B<int>; <-with this, namepace error
B<int> myB_;
};
template <> class A::B<int> {
int test() {
return 1;
}
};
As it appears the compiler complains "The explicit specialization "class A::B" must be declared before it is used."
If I try to provide the froward declaration in the commented line, the compiler complains
"The explicit specialization "B" must be declared in the namespace containing the template."
We use 2 different compilers here. This error is from IBM's "xl" compiler on AIX but I get similar errors with different verbage when compiling on our Sun systems.
It seems like a catch-22.
Obviously, this is a highly contrived, simplistic example but, it represents the problem.
I want to define a template class within a class because the template class is only relevent to the containing class. There should be no access to the template from outside the class.
Am I missing something?
You are correct. This is not possible to do (as far as I know). Your member declaration causes an implicit instantiation before the explicit specialization was declared. But how would you want to declare it? You cannot do that in class scope. Others have felt that this is an ugly restriction.
You could work around this by making the class member a pointer. This would not need to implicitly instantiate the class at that point, but rather at the point where you create the object in the end. I realize that this is an ugly work around. So better find other ways to do this.
For instance partial specializations are allowed in class scope. So you could add a dummy template parameter, then you can specialize this in the class before the member declaration. Likewise, i find this ugly, but it would not disturb things that much, I feel.
You could work around the issue by using an unnamed namespace for privacy:
namespace {
template <typename T>
class B {
int test() { return 0; }
};
template <> class B<int> {
int test() {
return 1;
}
};
}
class A {
B<int> myB_;
};
This will compile, but if A needs to be visible outside this compilation unit, you'll need more elaborate machinery (e.g., interface and factory or Pimpl).
B is not a template class and you are trying to specialize it. That is the cause for the error. You can check these two errors C2913 and C3413.
Is this what you are looking for?
class A
{
template<class T>
class B
{
inline int test()
{
return 0;
}
};
A::B<int> myB_;
};