This question already has answers here:
How can a class inherit from a template based on itself?
(3 answers)
Closed 5 years ago.
For example, would the following cause any errors during runtime?
template<typename Ty>
struct Foo {
};
// would this work during run time?
struct Bar : public Foo<Bar> {
};
If it does work, is it bad practice? Is it unoptimized?
Bar is declared at the point of struct Bar appearing. It is an incomplete type until the class definition is complete.
Your code is legal and is in fact a known design pattern called Curiously recurring template.
Your code doesn't cause any errors. Its called as curiously recurring template pattern.
It is used to create a specialised base class definition for each of the sub class instead of a common base class definition.
Consider the example:
template<class T>
class Base
{
static int counter;
public:
Base()
{
++ counter;
}
~Base()
{
-- counter;
}
static int getCounter()
{
return counter;
}
};
template<class T>
int Base<T>::counter = 0;
class A : public Base<A>
{
};
class B : public Base<B>
{
};
int main()
{
A a1, a2, a3;
B b1, b2;
cout << "A count is : " << A::getCounter() << endl;
cout << "B count is : " << B::getCounter() << endl;
}
Without curiously recurring template pattern, the output would be 5 and 5 for both classes (A & B)
With the said pattern applied, the output will be 3 and 2 for the A & B classes respectively.
Hope this helps :-)
Just like any other situation where you have a incomplete type (like when a class is forward declared), you cannot use the full object in the template. Logically it cannot be allowed because it would cause infinite recursion trying to populate a data type which includes itself.
template<typename Ty>
struct Foo {
Ty ty1; // <== Not allowed, incomplete type
Ty * ty2; // <== Just fine
Ty & ty3; // <== Just fine
};
struct Bar : public Foo<Bar> {
};
Live: https://godbolt.org/g/sf72Wp
It's no different than a class which uses itself:
Class Foo {
Foo * f;
};
Here is what the OP asked:
For example, would the following cause any errors during runtime?
template<typename Ty>
struct Foo {};
// would this work during run time?
struct Bar : public Foo<Bar> {};
If it does work, is it bad practice? Is it unoptimized?
What is shown in the question is that there is an empty struct Foo that is of a template type. The only thing this struct has is a default ctor & dtor, there is no consumption of stack memory outside of the default ctor & dtor for there are no members. This by itself will compile absolutely fine and will not cause any runtime error. The next line in question struct Bar : Foo<Bar>{} is an empty struct Bar that is not a tamplate type that derives from a template type that happens to be instantiated as a class template of type <Bar> and again Bar has the same operability as Foo because it has only a default ctor & dtor. So this would still not cause any problems at runtime.
Now as for the last question and its two parts. If it does work, is it bad practice? Is it unoptimized?
What do you mean by unoptimized?
If it does work, is it bad practice?
Well this is the more important question...
Why?
It doesn't matter in this context until you start adding components to the classes. As it stand as is with these declarations, there are no executions or instructions happening after and before the ctor & dtor calls. These as they are, are both its declaration & definition since there are no member variables or methods to perform any calculations, comparisons or anything else. There is no existing variable declaration of the class type being instantiated such as in this snippet.
template<typename T> class Foo{};
class Bar : public Foo<Bar> {};
int main () {
Bar bar; // <-- now the default ctor is used and `bar` is now a declared variable
// in memory of type class Bar;
// Next line of code here
return 0;
} // -> default dtor is used
And this should still compile and run because of the fact that as these classes currently stand: bar.* or bar->* where * means any member is meaningless in this context since both classes here have a scope that is completely empty and there is nothing to do or perform.
Now when you start to add members of different types and methods to work on them then some things will work and others wont. For it to work Bar must be a complete type as you can not use an abstract class in this case. There might be a little more involved than this but this is as much as I can answer for this question. I hope this helps and if anyone sees any problems with this answer please leave a reply first as to what is wrong first and then let me address it before just down voting it right a way without giving a word. A negative comment can be more constructive than just a class Boo {};!
Related
When I inherit from a class the compiler has to know the definition of the base class in order to create it. But when I inherit from a template class using oneself (the inheriting class), how can the compiler create the code? It does not know the size of the class yet.
#include <iostream>
template <class T> class IFoo
{
public:
virtual T addX(T foo, double val) = 0;
// T memberVar; // uncomment for error
};
class Foo : public IFoo<Foo>
{
public:
Foo(double value)
: m_value(value) {}
Foo addX(Foo foo, double b) override
{
return Foo(foo.m_value + b);
}
double m_value;
};
int main()
{
Foo foo1(1);
Foo foo2 = foo1.addX(foo1, 1);
std::cout << foo2.m_value;
}
First I thought it works because it's an interface but it also works with a regular class.
When I store the template as a member i get an error that Foo is undefined, just as I expected.
The general concept here is called the Curiously Recurring Template Pattern or CRTP. Searching on that will get lots of hits. see: https://stackoverflow.com/questions/tagged/crtp .
However there is a simple explanation that likely answers your question without getting too much into CRTP. The following is allowed in C and C++:
struct foo {
struct foo *next;
...
};
or with two types:
struct foo;
struct bar;
struct foo {
struct bar *first;
...
};
struct bar {
struct foo *second;
...
};
So long as only a pointer to a struct or class is used, a complete definition of the type doesn't have to be available. One can layer templates on top of this in a wide variety of ways and one must be clear to reason separately about the type parameterizing the template and its use within the template. Adding in SFINAE (Substitution Failure Is Not An Error), one can even make templates that do no get instantiated because things cannot be done with a given type.
With this definition of template class IFoo, the compiler does not need to know the size of Foo to lay out IFoo<Foo>.
Foo will be an incomplete class in this context (not "undefined" or "undeclared") and usable in ways that any incomplete type can be used. Appearing in a member function parameter list is fine. Declaring a member variable as Foo* is fine. Declaring a member variable as Foo is forbidden (complete type required).
how can the compiler create the code?
Answering this question would be the same as answering this question: How can the compiler compile that?
struct Type;
Type func(Type);
Live example
How can you define a type that doesn't exist and yet declare a function that use that type?
The answer is simple: There is no code to compile with that actually use that non-existing type. Since there is no code to compile, how can it even fail?
Now maybe you're wondering what is has to do with your code? How does it make that a class can send itself as template parameter to it's parent?
Let's analyze what the compiler see when you're doing that:
struct Foo : IFoo<Foo> { /* ... */ };
First, the compile sees this:
struct Foo ...
The compiler now knows that Foo exists, yet it's an incomplete type.
Now, he sees that:
... : IFoo<Foo> ...
It knows what IFoo is, and it knows that Foo is a type. The compiler now only have to instanciate IFoo with that type:
template <class T> struct IFoo
{
virtual T addX(T foo, double val) = 0;
};
So really, it declares a class, with the declaration of a function in it. You saw above that declaring a function with an incomplete type works. The same happens here. At that point, Your code is possible as this code is:
struct Foo;
template struct IFoo<Foo>; // instanciate IFoo with Foo
So really there's no sorcery there.
Now let's have a more convincing example. What about that?
template<typename T>
struct IFoo {
void stuff(T f) {
f.something();
}
};
struct Foo : IFoo<Foo> {
void something() {}
};
How can the compiler call something on an incomplete type?
The thing is: it don't. Foo is complete when we use something. This is because template function are instantiated only when they are used.
Remember we can separate functions definition even with template?
template<typename T>
struct IFoo {
void stuff(T f);
};
template<typename T>
void IFoo<T>::stuff(T f) {
f.something();
}
struct Foo : IFoo<Foo> {
void something() {}
};
Great! Does it start looking exactly the same as your example with the pure virtual function? Let's make another valid transformation:
template<typename T>
struct IFoo {
void stuff(T f);
};
struct Foo : IFoo<Foo> {
void something() {}
};
// Later...
template<typename T>
void IFoo<T>::stuff(T f) {
f.something();
}
Done! We defined the function later, after Foo is complete. And this is exaclty what happens: The compiler will instanciate IFoo<Foo>::stuff only when used. And the point where it's used, Foo is complete. No magic there either.
Why can't you declare a T member variable inside IFoo then?
Simple, for the same reason why this code won't compile:
struct Bar;
Bar myBar;
It doesn't make sense declaring a variable of an incomplete type.
Here a brief example of a code that works. It helps to introduce the actual question.
The specifiers for the visibility are the same used in the real code.
class Base {
public:
using foo = int;
virtual ~Base() { }
protected:
static foo bar() noexcept {
static foo v = 0;
return v++;
}
};
template<class Derived>
class Class: public Base {
static foo get() noexcept {
static foo v = bar();
return v;
}
};
int main() { }
It follows the previous example, even though slightly modified.
A template parameter has been added to the base class and the derived one has been updated accordingly.
This one doesn't compile.
template<typename T>
class Base {
public:
using foo = int;
virtual ~Base() { }
protected:
static foo bar() noexcept {
static foo v = 0;
return v++;
}
};
template<class Derived, typename T>
class Class: public Base<T> {
static foo get() noexcept {
static foo v = bar();
return v;
}
};
int main() { }
The error is quite clear indeed and the problem is not to solve it:
main.cpp:18:12: error: ‘foo’ does not name a type
static foo get() noexcept {
^
main.cpp:18:12: note: (perhaps ‘typename BaseComponent<T>::foo’ was intended)
The actual question is: why foo is not visible without a scope specifier in the second example?
I mean, Class is derived from Base<T>, that is (at least in my mind) a fully defined type, thus foo should be part of the base class and thus visible to the derived one, as it happens in the first example.
It follows an example that, according to what I guess of the problem, actually compiles and should not, or at least should behave like the previous one:
template <typename T>
struct B {
using foo = T;
};
struct D: public B<int> {
static foo bar;
};
int main() {
B<int> *b = new D;
}
Could it be due to the fact that the derived class is not a templated one in this case?
Honestly, it seems to me a bit strange, because the foo type is part of the base class that is still a templated one, so it shouldn't be much different from the previous one.
It goes without saying that I'm wrong, but I cannot figure out what's wrong in my thoughts.
Thank you in advance for the help.
That's because of name lookup rules. If your base class is a template, the unqualified name in base is not resolved. The reason is that later in your code you may have a template specialization of that base which doesn't define the name, or for which the name means something completely different. It's too complicated for the compiler to figure out whether you have a specialization, and if so, whether your name means the same thing in it. So it prefers to defer the name lookup. To make the compiler "believe you", then you need to either use a qualified name, or this->name instead (for members, not for typedefs), and the compiler will resolve the name.
I'm puzzled by the following problem. I want to write some trait struct in order to test if a certain class is derived from another. This can be solved with boost::is_base_of<>. However, the base class I want to test against has a free undefined template parameter.
Here is some code example:
template<typename T> class Base {};
class IntDeriv : Base<int> {};
class Foo {};
template< class TestClass >
struct is_derived_from_Base {
// how to create something that does the following and forces the compiler to deduce T
static const bool value = boost::is_base_of< Base<T> , TestClass >::value;
};
int main() {
cout << is_derived_from_Base<Foo> << endl; // should print 0
cout << is_derived_from_Base<IntDeriv> << endl; // should print 1
}
The problem is how to deduce T for Base<T> inside is_base_of.
Is this possible at all? I smell some enable_if but I'm not sure how to put it together.
What you want is possible. The trick used is possible in C++03, but as you didn't specify I will give you the C++11 version (using decltype, not available in C++03):
template<class TestClass>
struct is_derived_from_Base
{
template<typename T>
static std::true_type inherited(Base<T>*);
static std::false_type inherited(void*);
static const bool value = decltype(inherited(new TestClass()))::value;
};
You can see a live version here.
So, how does it works ?
When the struct is instantiated and value is needed, the compiler will get the return type of inherited(new TestClass()). This will call the appropriate function: if TestClass inherits Base<T>, then TestClass* (returned by new) is castable to Base<T>*, T being automatically deduced by the compiler. The return type is std::true_type. If TestClass does not inherits Base<T>, then the other overload is choosen, and the return type is std::false_type. The rest is straightforward: std::true_type::value = true and std::false_type::value = false.
There are still some corner cases:
Using private inheritance results in a compile error. I do not know how to solve it, because I don't know the answer: if A privately inherits B, is A derived from B or not ? (in general, private inheritance is considered as an implementation inheritance). Also note that in this case, A* a = new B(); will not compile.
Using a private constructor prevents the previously explained trick from working correctly. It will result in a compile-time error. Since this defeats the whole point of this method, you will have to find another way for such classes.
Note you have to use it the following way: is_derived_from_Base<Foo>::value, not as you've written (is_derived_from_Base<Foo>).
In the sample:
#include <iostream>
using namespace std;
class B
{
public:
virtual void pvf() = 0;
};
template <class T>
class D : public B
{
public:
D(){}
virtual void pvf() {}
private:
string data;
};
template <>
class D<bool> : public B
{
public:
D();
virtual void pvf(){ cout << "bool type" << endl; }
};
int main()
{
D<int> d1;
D<bool> d2;
}
I get the following error:
test.cpp:(.text+0x1c): undefined reference to `D<bool>::D()'
Note that the reason I don't just specialize the D() by itself is I want to eliminate the need for string D<T>::data in the D<bool> case.
Why do I need to re-implement D() in D<bool>? Seems like there should be a way for me to tell the compiler to use the version from D<T>.
Is there any way to do a simple specialization like this without having to re-implement methods?
Each specialisation of a class template gives a different class - they do not share any members with each other. Since you've explicitly specialised the entire class, you don't get any of the members from the template, and must implement them all.
You can explicitly specialise individual members, rather than the entire class:
template <> void D<bool>::pvf(){ cout << "bool type" << endl; }
Then D<bool> will still contain all the members of the class template that you haven't explicitly specialised, including the default constructor.
No, there is not.
Specialization is behaving very differently than inheritence. It has no connection to the generic template version.
When you use/instantiate a template, the compiler will create a new type name, and then look for how this type is defined. When it finds a specialization, then it takes that as the definition for the new type. When it does not, it takes the generic template and instantiates it.
Therefore they have no connection, and you are just writing an entirely new class, just with a special name for the compiler to find in case of someone using/instantiating the template to find it under that name.
The problem is your erroneous assumption that there is anything common between D<A> and D<B>. Template instances are types, and two different instances are two different types, end of story. It only so happens that instances of the same template have formally similar code, but with specialization you can define any type you like. In short, every type that you define explicitly is entirely independent, and there is no commonality across specialized template instances, even if they happen to have the same name.
For example:
template <typename T> struct Foo
{
T & r;
const T t;
void gobble(const T &);
Foo(T *);
};
template <> struct Foo<int>
{
std::vector<char> data;
int gobble() const;
Foo(bool, int, Foo<char> &);
};
The types Foo<char> and Foo<int> have nothing to do with one another, and there is no reason why any part of one should have any use inside the other.
If you want to factor out common features, use private inheritance:
template <typename> struct D : private DImpl { /* ... */ }
You need to reimplement it because D<T> and D<bool> are totally unrelated classes (they just happen to "share the name"). That's just how templates work.
If you want the classes to share construction code, just put that code inside B::B (i.e. the same thing you do every time you want to reuse code in different branches of the same hierarchy: move the code up and let inheritance handle the rest).
Consider that D<T>::D() will be responsible for default-constructing string data, and that D<bool> doesn't have any such member. Clearly there is no way to use the same emitted code in each case.
However, if your default constructor doesn't do anything (in either version here), just omit it and allow the compiler to do the work.
C++ templates allow we put whatever in our objects with template arguments. However, if our template arguments use functions/variables which belong to certain types, how do we check?
template<typename BarType>rguments
class Foo {
public:
Foo() { bar = new BarType() }
private:
BarType * bar;
}
Foo<Bar> …
BarType could be anything derived from a Bar superclass.
What happens if we invoke some functions which only belong to Bar in our Foo class? What happens if we pass non-BarType? Do we have anyway to check?
You will get a compile-time error if your templated code makes reference to members that the actual parameter doesn't provide when you try to instantiate the template. So don't worry, you won't be able to break anything.
Think of templates as a code-generation mechanism. Whether the generated code actually makes sense can sometimes only be determined when you actually try it.
Given the class template TempFoo below, you see that it calls the example function of the templated T type in its constructor. The first two types work because they both define example; the third doesn't.
template<typename T>
class TempFoo
{
void TempFoo() {
T obj;
obj.example();
}
};
class First {
void example() {}
};
class Second {
void example() {}
};
class Third {
};
int main()
{
TempFoo<First> f; // works
TempFoo<Second> s; // works
TempFoo<Third> t; // doesn't
}
Whatever you're giving in the tempalte parameter list is simple a place place holder. Compiler will repalce with appropriate types according to the type of object which is used to instantiate the template class. A compile-time error will be appear if the object doesn't satify the operations done in the functions. Also it's a good practice to use 'T' as place holder.
An easy way to ensure that the template parameter derives from the base class:
template<typename BarType>
class Foo
{
// ...
~Foo() { Bar* p = (BarType*)0; }
};
The compiler will type-check the assignment, generating an error if Bar isn't an unambiguous supertype of BarType, and then optimize away the unused local variable.