In the code below, I get a compiler error when trying to push fooBaz onto v. This surpises me since Baz is a derived class of Bar.
Why is this not allowed, and what can I do if want to put several Foo instances, templated on classes derived from the same base class, into a vector?
#include <iostream>
#include <vector>
template<typename T>
class Foo {};
struct Bar {};
struct Baz : public Bar {};
int main() {
Foo<Bar> fooBar;
Foo<Baz> fooBaz;
std::vector<Foo<Bar>> v;
v.push_back(fooBar);
v.push_back(fooBaz);
return 0;
}
Java Generics are not the same kind of thing as C++ templates.
C++ values of class type are not the same thing as Java reference variables of class type.
You are running into both problems here.
C++ templates generate a new, unrelated type for each set of template arguments. You can create a common base, but you have to do it yourself.
Java Generics, under the hood, actually create a single class. It then writes casting operations at inputs and outputs.
So a Java Generic, Foo<Base> and Foo<Derived> are related, because the the Java Generic actually creates a Foo<Object> then wraps it up in casts, and those casts in Foo<Base> and Foo<Derived> are compatible. (well, not always Object, you mark up the generic arguments with information that Java uses to determine what the actual type it writes its Generic for, but that gives you the idea).
In C++, there is no relation. (well, template pattern matching gives you a compile-time relation, but no runtime relation at all)
The second problem is that you are treating values of class type like references. In C++, a Foo is an actual foo. It represents a block of memory that is an instance of that class. In Java, a Foo is a smart pointer to an object on the heap somewhere that obeys the Foo protocol (is a derived class).
You cannot easily make a value of type Foo in Java, and you cannot easily make a mark and sweep smart pointer to a Foo in C++.
Foo<Bar> fooBar;
Foo<Baz> fooBaz;
these are two unrelated types. They are stored on the stack (automatic storage).
std::vector<Foo<Bar>> v;
This stores a buffer of memory containing Foo<Bar> objects packed together.
v.push_back(fooBar);
This copies a fooBar instance from automatic storage into the vector.
v.push_back(fooBaz);
This doesn't work, because fooBar and fooBaz are unrelated types.
Now, prior to c++23 reflection, mimicing what Java does is difficult in C++. You have to do some steps manually.
First, instruct Foo to understand inheritance when told so manually:
struct empty_t {};
template<class T, class Base=empty_t>
class Foo:Foo<Base> {};
template<>
class Foo<empty_t, empty_t> {
virtual ~Foo() {}
};
struct Bar {};
struct Baz : public Bar {};
auto fooBar = std::make_unique<Foo<Bar>>();
auto fooBaz = std::make_unique<Foo<Baz, Bar>>();
std::vector<std::unique_ptr<Foo<Bar>>> v;
v.push_back(std::move(fooBar));
v.push_back(std::move(fooBaz));
this compiles.
In c++23 compile time reflection should let you auto-detect the base classes of Baz and have Foo<Baz> automatically inherit from Foo<Bases>... if you want.
Now, inheritance is only one kind of way to handle polymorphism in C++, but I think is enough for today.
As mentioned in a comment, templates are like recipes. You need to instantiate a class template to get a class, before it is just a template. There is no implicit relation between different instantiations (other than being instantiations of the same template). Maybe this gets more clear when you consider following example:
template<typename T> struct Foo {};
template <> struct Foo<int> { void bar(){} };
template <> struct Foo<double> { void moo(){} };
int main() {
Foo<int> x;
x.bar();
Foo<double> y;
y.moo();
}
Foo<int> and Foo<double> are two unrelated types with completely different methods. If they were not instantiations of a template, but "ordinary" types with different members you would not be surprised that you cannot push a FooA into a std::vector<FooB>.
A std::vector<Foo<Bar>> can only hold elements of type Foo<Bar>. The vector is not aware that this type is the result of instantiating the template Foo. And even if it was, you cannot assign a Foo<Bar> to a Foo<Baz> unless you provide a conversion.
Actually there is more to your misunderstanding that needs to be debunked. Suppose you have a std::vector<Bar> then you also cannot push a Baz to that vector. See here for why not: What is object slicing?. And here for what to do instead: https://stackoverflow.com/a/16126649/4117728.
Related
Note: I am using C++11
Suppose that I have a class foo with public variable bar which can be either a float or an int. The type of bar is determined upon assignment like so:
foo object_name;
object_name.weight() = value
if value is an int, then object_name.weight() is an int, if value is a float, then object_name.weight() is a float.
One possibility is to have two classes foo_i and foo_f with bar typed as int and float, respectively; but this is rather inconvenient, especially when foo_i and foo_f are basically clones of one another with only the type difference between them.
Is there a way to specify the type of bar as either int or float within the scope of a single class?
In order to write the class body only once, you can template the class foo on the type of bar, and then use two typedefs to simplify subsequent usage. Example:
template<typename TBar> class foo {
public: TBar bar;
};
typedef foo<int> foo_i;
typedef foo<float> foo_f;
Of note for those new to C++ is that this metaprogramming technique will literally make the compiler generate two separate versions of the same class, i.e., from the perspective of the generated binary, it is equivalent to writing the class body twice with two different types for bar.
I have a templated class where I ensure the type parameter is a subclass of some abstract base class like this:
#include <type_traits>
template<typename T>
class Foo
{
static_assert(
std::is_base_of<MyBase, T>::value,
"T must be a descendant of MyBase"
);
void SomeMethod()
{
auto bar = T();
//or
auto bar = T("Constructor with parameter")
bar.someFunctionOnMyBase();
}
};
Now in programming languages like C# or Java I can use this information and use he type information to call methods on the template type. Is such thing possible? Bonus points if it also possible to call constructors with the correct parameters.
Yes, this is totally fine (as long as both the constructor that you intend to call and the destructor of class T are publicly accessible).
As a matter of fact you don't even need to have the static_assert, and as long as a member function T::someFunctionOfMyBase exists (even if it isn't the one defined in MyBase and just happens to share the name), this will still compile.
Suppose we have a templated class,
template<typename Type>
class Container {};
Of course, we can't do this:
struct Foo
{
Container _container;
}
But what if we wanted to do something like it? Is the only way to do this, to define a base class,
class ContainerBase {};
template<typename Type>
class Container : public ContainerBase {};
and store a pointer, like below?
struct Foo
{
ContainerBase* _container;
}
It's simple enough, but it feels weird to have to add a base class solely for that reason, when it seems the compiler should have enough information to imply a set of related specializations. Of course, regardless _container needs to be a pointer, else Foo couldn't resolve to a static size, but
struct Foo
{
Container* _container;
}
doesn't work either.
it seems the compiler should have enough information to imply a set of related specializations.
Nope. Template specializations are totally unrelated except in name, and the name of a type has essentially no bearing on runtime operation. Specializations of a given template usually share a (mostly) common interface, but they could just as well be completely different.
Adding a base class is essential if you want to relate between the specializations. And if they share so much in common, factoring that functionality into the base is a pretty great idea.
I have two templates A and B both with the same template types. Always, A inherits from B, but it always chooses the same template for B as A itself uses.
This is fine in and of itself, but this requires me to write the template type twice. Is it possible to somehow typedef the type in A and refer to the generic typedef name when inheriting from the subclass?
Below is some example code that does not compile but should give a clear idea of what I want to do:
// #1
template <typename T>
struct A
{
typename T type;
// class details here
};
// #2
template <typename T>
struct B
{
// class details here
};
// #3
template <>
struct A<int>
: B<type> // Compiler complains here (type not defined)
//: B<A::type> // Compiler complains here (type not defined)
// I could write ": B<int>" instead, but this is repitition I want to avoid
{
// class specialization details here
};
I'm open for alternative solutions. The reason this is important to me is that I have a large laundry list of code like that of #3 and I want to reduce duplication (to avoid bugs).
Two different specializations of the same template are completely unrelated types*, so you cannot use A<>::type from the base template inside the specialization A<int>. Even if you define type inside the A<int> specialization, it won't be available until the class is defined, which happens after the list of inheritance.
You could and you should use : B<int> there. It is no more repetition than B<type>, and makes it explicit that A<int> inherits from B<int>, something that is not immediately visible if you go through indirections.
Another thing that kind of bugs me from your design is that A<> (the generic one) does not have any relationship with B<>, but A<int> inherits from B<int>. While the language allows for completely unrelated behavior in specializations, that might be surprising to other programmers, when they can pass objects of A<T> to functions taking B<T> for some T, but not for others...
What is the actual problem that you want to get solved?
* The implication of this is that an specialization does not provide special behavior (i.e. only the bits that differ from the base) but all the behavior of that type. If you only mean to override part of the behavior you should consider other alternatives, like refactoring A<T> (generic) into a base so that A<int> (or other specializations) can borrow the implementation. Or if the changes in behavior are small you might be able to just specialize some of the member functions...
I have got two classes.
The first class (A) is builded with an template.
template <class T>
class A
{
public:
T value;
};
The second class (B) should have an object of class A as member variable. Like this:
class B
{
public:
A<int> value;
};
But now i want to use any kind of template-class in class A. Not only int.
Apparent I can't declare a (member-)variable which contains any kind of a class.
So, I need something like this:
class B
{
public:
A<*> value;
};
Is there any (clean) solution for this problem?
-- Greeting from Germany, Bastian
You cannot have a single class B with "any" member object, because B has to be a well-defined class, and A<T> is a different type for different types T. You can either make B a template itself:
template <typename T>
class B
{
A<T> value;
};
or you can take a look at boost::any, which is type-erasing container for arbitrary types (but making use of it requires a certain amount of extra work). The any class only works for value types, though, it's not completely arbitrary.
The simplest solution would be to make all A variants ineherit from a common interface, even if it's empty :
class IA{}
template <class T>
class A : public IA
{
public:
T value;
};
class B
{
public:
IA* value;
};
Now, the associated costs:
interactions with value are limited to the IA interface;
if you try to cast to get the real type, that mean that you know the real type, so it's of no use and make A type a parameter of B becomes really easier to use.
there are runtime costs associated to runtime inheritance
Advantage :
it's easily understood by other developers
it naturally limit the types possible to some specific ones
it don't use boost (sometimes, you just can't)
So to do better there are other less simple solutions but that are simple enough to be used :
If you can use boost, boost::any, boost::variant and boost::mpl might be base of solutions.
Boost any can be used as a safe replacement to void*. The only problem with this is that you can have ANY type, like if the type was a template parameter of the B class.
Boost variant might be used successfully if you know all the types that A can be.
MPL might be helpful if you just want to set a list of possible types and make sure your members apply only to them. You can do a ton of things with MPL so it really depends on your exact needs.
You've got two choices, I think. The first is to parameterize your class over the type parameters of the instance variables:
template <class T> struct B
{
A<T> value;
};
The other option is to declare value as a void* pointer. (But that's probably not what you want).
yes, it's already been done. boost::any.
I think it helps to understand, that templated classes create an entirely new and seperate class for every type you use with it. For instance, Vector<int> and Vector<float> are as separate as the classes VectorInt and VectorFloat.
For class B, you are basically asking that the value variable either be A<int> or A<float>, which is the same as saying you want value to either be a "A_int" or "A_float". And to accomplish that you... well, use another template!