How to find the largest possible sizeof child class - c++

Given a class, I would like to find the largest sizeof() all child classes of it in compile-time. In this case, you will need to properly define the value of B::BIGGEST_TYPE_SIZE, preferably in the class itself.
It is possible to do so in a separate chunk of code with the usage of std::max() as shown in the last line, but it's some what duplicate code and unelegant, as I will have to continuously modify that line as more classes inherit from B.
I would like a nice scalable solution instead.
struct B
{
static const int BIGGEST_TYPE_SIZE;
};
struct D1 : public B
{
int i;
};
struct D2 : public B
{
std::vector<int> vec;
};
struct D3 : public B
{
std::string s;
};
const int B::BIGGEST_TYPE_SIZE = std::max(sizeof(D1), std::max(sizeof(D2), sizeof(D3)));
The value of BIGGEST_TYPE_SIZE should be "32", due to std::string.
Any elegant solutions for this?
The sexier the templates, the better.
Thanks!

Take for example std::variant, it knows its size from the template arguments. Your best shot is also to use a variadic template. First, you implement the variadic max function template, then you use it:
template <typename ... Ts>
constexpr bool biggest_size_v = max(sizeof(Ts)...);
If you're asking to automatically get a list of all derived classes at compile-time. You can't. You still have to list them:
const int B::BIGGEST_TYPE_SIZE = biggest_size_v<D1, D2, D3>;

It is possible to do so in a separate chunk of code with the usage of std::max as shown in the last line, but it's some what duplicate code and unelegant, as I will have to continuously modify that line as more classes inherit from B.
I would like a nice scalable solution instead.
Unfortunately, I don't know a way to automatically know all derived types (I don't think it's possible) so I fear that you needs "to continuously modify that line as more classes inherit form B".
In LogicStuff's answer you see an elegant way to simplify that line and I also remember that exist the std::max() version that receive a std::initializer_list (constexpr starting from C++14) so you can also write (but the biggest_size_v way is better, IMHO)
const int B::BIGGEST_TYPE_SIZE
= std::max({sizeof(D1), sizeof(D2), sizeof(D3)});
avoiding the multiple std::max() calls.
A little off topic, I suppose, but I propose you a semi-automatic way to check, compile-time, that B::BIGGEST_TYPE_SIZE is bigger (or equal) to the sizeof() of all derived types (all instantiated derived type, at least).
If you modify B adding a constructor with a static_assert() in it (or SFINAE enabled, if you prefer)
struct B
{
static const int BIGGEST_TYPE_SIZE;
template <std::size_t DerSize>
B (std::integral_constant<std::size_t, DerSize>)
{ static_assert( DerSize <= BIGGEST_TYPE_SIZE, "!" ); }
};
and add a template C struct that inherit from B
template <typename Der>
struct C : public B
{
C() : B{std::integral_constant<std::size_t, sizeof(Der)>{}}
{ }
};
if you modify your Dx classes to inheriting B passing through C<Dx> (so using CRTP)
struct D1 : public C<D1>
{ int i; };
struct D2 : public C<D2>
{ std::vector<int> vec; };
struct D3 : public C<D3>
{ std::string s; };
you auto-magically enable the compile-time check inside B constructor.
So if you add, by example, the following D4 class
struct D4 : public C<D4>
{ int a[42]; };
and forget to modify the BIGGEST_TYPE_SIZE initialization adding sizeof(D4) in the list, declaring a D4 object you get a compilation error
D4 d4; // compilation error
The following is a full compiling example
#include <vector>
#include <iostream>
#include <algorithm>
struct B
{
static const int BIGGEST_TYPE_SIZE;
template <std::size_t DerSize>
B (std::integral_constant<std::size_t, DerSize>)
{ static_assert( DerSize <= BIGGEST_TYPE_SIZE, "!" ); }
};
template <typename Der>
struct C : public B
{
C() : B{std::integral_constant<std::size_t, sizeof(Der)>{}}
{ }
};
struct D1 : public C<D1>
{ int i; };
struct D2 : public C<D2>
{ std::vector<int> vec; };
struct D3 : public C<D3>
{ std::string s; };
struct D4 : public C<D4>
{ int a[42]; };
const int B::BIGGEST_TYPE_SIZE
= std::max({sizeof(D1), sizeof(D2), sizeof(D3)}); // <-- sizeof(D4) forgotten !!!
int main ()
{
D1 d1;
D2 d2;
D3 d3;
// D4 d4; compilation error
}

Related

How do I put items into a vector which has a type of a templated super class

Assume I have the following 2 classes Super and Deriv where Deriv is a subclass of Super.
Super and Deriv are both templated classes. I want to create a vector of type Super<?>. Where the ? signifies any type. Currently I have come up with this:
#include <iostream>
#include <vector>
template <typename T>
class Super {
public:
T val;
Super(T val) : val(val) {};
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<Super*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
std::cout << a[0]->val << std::endl;
return 0;
}
Of course this does not work because std::vector<Super*> needs a template type like std::vector<Super<int>*>. However the problem with this is I can only add items to the vector of type Super<int>* and not Super<float>*.
How can I alter this code to allow me to add a Super type or it's derivatives to a vector which has any template type like int, float, short etc?
Not sure if this can solve your problem, but it might give you some ideas. The basic idea here is to make a super class for all kinds of T. For fundamental data types, wrapper classes are needed.
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
class SuperT {
public:
virtual std::string AccessData() = 0;
};
class IntWraper : public SuperT {
public:
IntWraper(int i) : val(i) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
int val;
};
class FloatWraper : public SuperT {
public:
FloatWraper(float f) : val(f) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
float val;
};
class RealSuper {
public:
virtual std::string DoSomething() = 0;
};
template <typename T>
class Super : public RealSuper {
public:
T* wraper_val_;
Super(T* w_val) : wraper_val_(w_val) { };
~Super() { if(wraper_val_) delete wraper_val_; };
virtual std::string DoSomething() { return wraper_val_->AccessData(); }
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T* w_val) : Super<T>(w_val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<IntWraper>(new IntWraper(1)));
a.push_back(new Deriv<FloatWraper>(new FloatWraper(1.0f)));
std::cout << a[0]->DoSomething() << std::endl;
std::cout << a[1]->DoSomething() << std::endl;
return 0;
}
This
std::vector<Super*> a;
Is wrong.
There is no type named Super in your code. Yes, you declared something named "Super", but it's not a class, it's a class template.
The name of the feature says it all. It's a template. The compiler will use Super to generate new types at compile time.
For example, Super<int> Refer to the class generated by the compiler when filling the hole T in the Super template.
So why a[0]->val cannot possibly work? Well, imagine this:
template<>
struct Super<std::string> {
std::string my_val;
};
We specialize Super so when instantiated with std::string, it no longer have the val member, but the my_val member.
Now, what do you expect this code to do?
std::vector<Super*> a;
a.push_back(new Deriv<std::string>);
std::cout << a[0]->val << std::endl;
Quite puzzling isn't it? You'd need a compilation error at runtime. Since the existence (or non existence) of variable is determined at compile time, it's not possible.
Now how can we solve your problem?
In your case, it would be as simple as adding an interface above Super, and exposing functions that implements behaviors needed to do your calculations:
struct Interface {
void print(std::ostream) const = 0;
bool lessThan(double) const = 0;
};
template <typename T>
struct Super : Interface {
T val;
Super(T val_) : val{val_} {};
// We implement the needed behavior.
void print(std::ostream o) const override {
o << val << std::endl;
}
// Example of calculation
bool lessThan(double rhs) const override {
return val < rhs;
}
};
Now you can do:
std::vector<Interface*> a;
// ...
a[0]->print(std::cout);
a[0]->lessThan(3.7);
Create a new class RealSuper that cache the type is a possible workaround.
It is not perfect, but I am afraid that it can't be much better than this :-
class RealSuper{ //<-- new class
public: enum TYPE{ type_int, type_float, type_notIni }; // (yes, it is awkward)
TYPE typee = type_notIni;
};
template <typename T> class Super : public RealSuper { //<-- modify
public: T val;
Super(T val) : val(val) {
if( std::is_same<T, int>::value ){
typee = type_int;
}else if( std::is_same<T, float>::value ){
typee = type_float;
}
};
};
template <typename T> class Deriv : public Super<T> {
public: Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
for(auto ele: a){
switch( ele->typee ){
case RealSuper::TYPE::type_int: {
int value=static_cast<Super<int>*>(ele)->val;
std::cout << value << std::endl;
};break;
case RealSuper::TYPE::type_float :{
float value=static_cast<Super<float>*>(ele)->val;
std::cout << value << std::endl;
};break;
}
}
return 0;
}
live demo
Here is another demo to show an approach using the virtual function.
C++ does not have the feature you are asking for (I believe it is a kind of reification). This means you cannot store arbitrary types in location one, then specify arbitrary operations in a completely unrelated source file in location two, then apply the operations to the data in location three.
There are many problems you can solve that are close to what you are asking for: Restrict what operations you want to do at location two, or restrict what types you store at location one, and the problem can be solved. Alternatively, restrict what type x operation pairs you support at location three.
Note that composition of the restricted set of operations can also work.
In theory you can embed a full compiler or interpreter in a C++ binary, compile code and dynamically load it. (This is basically how C#/Java manage reification). This is rather impractical for most problems. The language provides no support for this, but this is C++, you can do anything (write a Java/C# compiler even).
With no information about the underlying problem you need to solve, I cannot tell you which of the above is the correct approach.
This is the reason why "if I had X I coukd solve Y, but I cannot figure out X. I know, I'll just ask stack overflow how to do X!" is known as an X/Y problem. We can probably solve Y, but you asked about X which woukd solve Y without even describing Y. Feel free to post the Y problem and ask about it. Use the [ask a question] button above.
Restrict what types you handle at storage:
Store a std::variant. Use std::visit. Or write your own or use boost::variant.
Restrict what operations to perform:
Use type erasure to generate the per-type operation when you store the type.
Restrict the operation-type pairs at point of call:
Use RTTI to exctract what type is stored, have a large switch switch that then uses solution one.

Is there any better way to define a visitor pattern without a priori knowledge of derived classes?

This is my first encounter with the visitor design pattern for double dispatch. In my scenario objects derived from a common base class can interact somehow with each other, so the visitor and the visitee are from the same set. However, for practical reasons, my goal is to completely separate the logic from the application. In particular, I want to avoid at all costs the base class to be aware of what derived classes can be using it. There will be different independent application layers approaching the same base within one application.
The problem is that a virtual method needs to be defined in the base for each of the possible derived classes. This can be easily solved using templates. A bigger problem turned out to be that I don't want to be limited to a particular number (or upper bound) of derived classes. This is as close as I got:
/*** Generic ***/
template<class...>
struct Visit { };
template<class... Derived>
struct Base : Visit<Derived...> {
virtual int accept(Base*) = 0;
};
// specializations for different numbers...
template<class X>
struct Visit<X> {
virtual int visit(X*) = 0;
};
template<class X, class Y>
struct Visit<X, Y> {
virtual int visit(X*) = 0;
virtual int visit(Y*) = 0;
};
// and more for 3, 4, ...
/*** Application ***/
struct D1;
struct D2;
using A = Base<D1, D2>; // <--- the goal is to keep this simple
struct D1 : A {
int accept(A* a) { return a->visit(this); }
int visit(D1*) { return 1; }
int visit(D2*) { return 2; }
};
struct D2 : A {
int accept(A* a) { return a->visit(this); }
int visit(D1*) { return 3; }
int visit(D2*) { return 4; }
};
int main() {
A* d1 = new D1();
A* d2 = new D2();
return d2->accept(d1); // expected: 2
}
This works and satisfies most of the criteria except the last one. The maximum number of possible derived classes needs to be known in advance and hard-coded in the Visit template. And it's not very elegant to repeat the same boilerplate so many times just with different numbers of lines.
I was wondering whether something cleaner along the lines of
template<class X>
struct InjectVisit {
virtual int visit(X*) = 0;
};
template<class... Derived>
struct Base : InjectVisit<Derived>... {
virtual int accept(Base*) = 0;
};
(replacing the Visit template entirely) could be possible at all, in any variation, in C++. This, namely, does not work, for much the same reason as why partial specialization of function templates won't:
Overload resolution only selects a base template (or a nontemplate function, if one is available). Only after it's been decided which base template is going to be selected, and that choice is locked in, will the compiler look around to see if there happens to be a suitable specialization of that template available, and if so that specialization will get used.
Since each of the injected visit(X*) comes from a different template instantiation of InjectVisit, they won't compete against each other, leading to ambiguity errors (even though only exactly one of them could be used at any point).
I tried to adapt the second half of this answer but it won't work if D1 and D2 need to be derived from the same base (unless, again, all the deriveds are hard-coded into that). Of course, a dynamic_cast would be possible. But this code is meant to be called several 100,000's a second and I don't want RTTI to become my main bottleneck.
Currently I am stuck on a middle way in which the base class of Base is replaced by a single template class which needs to be provided, along the lines of Visit, by each application pattern separately, which seems to be the least evil, but I am still curious. Is just listing the names of a few classes and making C++ generate the few lines for me on demand really impossible?
Since each of the injected visit(X*) comes from a different template instantiation of InjectVisit, they won't compete against each other, leading to ambiguity errors (even though only exactly one of them could be used at any point).
You could use following using trick:
LIVE DEMO
#include <iostream>
void println(const char *s)
{
using namespace std;
cout << s << endl;
}
template<typename X>
struct InjectVisit
{
virtual void visit(X*) = 0;
};
template<typename Head, typename ...Tail>
struct VirtualChain : InjectVisit<Head>, VirtualChain<Tail...>
{
using InjectVisit<Head>::visit;
using VirtualChain<Tail...>::visit;
};
template<typename Head>
struct VirtualChain<Head> : InjectVisit<Head>
{
using InjectVisit<Head>::visit;
};
template<typename ...List>
struct Base : VirtualChain<List...>
{
virtual void accept(Base*) = 0;
};
/****************************************************************/
struct D1;
struct D2;
using ConcreteBase = Base<D1, D2>;
struct D1 : ConcreteBase
{
virtual void accept(ConcreteBase* visitor) { visitor->visit(this); }
virtual void visit(D1*) { println("D1 visited by D1"); }
virtual void visit(D2*) { println("D2 visited by D1"); }
};
struct D2 : ConcreteBase
{
virtual void accept(ConcreteBase* visitor) { visitor->visit(this); }
virtual void visit(D1*) { println("D1 visited by D2"); }
virtual void visit(D2*) { println("D2 visited by D2"); }
};
int main()
{
ConcreteBase* d1 = new D1();
ConcreteBase* d2 = new D2();
d1->accept(d2);
d2->accept(d2);
}
Output is:
D1 visited by D2
D2 visited by D2

Using multiple-inherited base class member names in derived class

I have classes A1, A2, and B:
struct A1 { int call(int); };
struct A2 { double call(double); };
struct B : public A1, public A2 {};
Due to the name 'call' being shared by A1 and A2, the compiler hides those names in B, even if the signatures aren't the same.
struct B { void do_stuff() { call(int(0)); /* call? call what? *ERROR* */ } };
So then I go ahead and throw down some using declarations:
struct B { using A1::call; using A2::call; void do_stuff() { call(int(0)); } };
And everything's hunky-dory. But what if A1 and A2 have a lot of similarly named members, ex. A1 and A2 are specializations for some template class, and we want B to have the members of both under one roof? Then it ends up looking... well, very boilerplate heavy.
struct B {
using A1::call; using A2::call;
using A1::asdf; using A2::asdf;
using A1::other_thing; using A2::other_thing;
...
};
I have to do this kind of a thing for tens of classes inheriting from lower-level class specializations. Is there any construct that would ease this keyboard tip-tappin' and readability hell?
Something like using A1::* if using declarations somehow supported globbing.
Update:
I've come up with a solution that recursively places using-declarations in a pseudo-variadic template hierarchy. It actually comes out really clean looking in the client code, looking something like this:
class B : mixins<A1,A2> {
void do_stuff() {
call(int(0)); // I'm not a compiler error!
call(double(0)); // neither am I!
}
};
I'll post the details as an answer if no one else comes up with anything.

Using template argument of base class in derived class

Consider the following situation in C++:
template<int n>
class Base { ... };
class Derived3 : public Base<3> {
// a complicated body, making use of n=3
};
class Derived7 : public Base<7> {
// a completely different body, making use of n=7
};
Inside of the Derived3 member functions, I would like to explicitly use n=3, and inside Derived7, n=7, without hardcoding the numbers, i.e., still referring to something like a template argument n. The following options come to my mind:
Also templating the derived classes on n, and then using typedef. This way, the derived classes know n:
template<int n>
class DerivedTemplate3 : public Base<n> { ... };
typedef DerivedTemplate3<3> Derived3;
template<int n>
class DerivedTemplate7 : public Base<n> { ... };
typedef DerivedTemplate7<7> Derived7;
The problem with this is that DerivedTemplateX makes sense for nothing but n=X, so this feels like abusing the template paradigm.
Using a static const member to store n in Base, and referring to that in the derived classes:
template<int n>
class Base {
protected:
static const int nn = n;
...
};
class Derived3 : public Base<3> {
// refer to nn=3
};
class Derived7 : public Base<7> {
// refer to nn=7
};
The problem here is that I seemingly can't use the same identifier (nn vs. n). Also, I'm not sure whether this will allow me to use nn as a template argument for members of the derived classes.
So: how can this be implemented in a non-redundant, efficient way? Maybe using some kind of static const int as a member somewhere?
The standard practice is to use an uppercase letter for the template parameter, then a static const value in lowercase:
template<int N>
class Base {
protected:
static const int n = N;
...
};
Then you use the lowercase static const value n everywhere - don't use N anywhere else.
Also, I'm not sure whether this will allow me to use nn as a template argument for members of the derived classes.
It is a constant expression and so it can be used as a template argument.
Does this work for you?
template<int n>
class Base {
protected:
static const int MyN = n;
};
class Derived3 : public Base<3> {
void f()
{
std::cout << MyN;
}
};
class Derived7 : public Base<7> {
void f()
{
std::cout << MyN;
}
};
int main()
{
}

Conditional variable declaration

I'm coming from Python and I have some problem with managing types in c++. In Python I can do something like this:
if condition_is_true:
x=A()
else:
x=B()
and in the rest of the program I can use x without caring about the type of x, given that I use methods and member variables with the same name and arguments (not necessary that A and B have the same base classes).
Now in my C++ code type A corresponds to
typedef map<long, C1> TP1;
and B to:
typedef map<long, C2> TP2;
where:
typedef struct C1
{
char* code;
char* descr;
int x;
...
}
and
typedef struct C2
{
char* code;
char* other;
int x;
...
}
C1 and C2 have similar members and in the part of code I'm talkin of I only have to use the ones with the same name/type
I would like to do something like:
if (condition==true)
{
TP1 x;
}
else
{
TP2 x;
}
what is the correct approach in c++?
thanks in advance
If the condition is known at compile-time, you can use std::conditional. This is useful in generic code.
typedef std::conditional<
std::is_pointer<T>::value
, TP1
, TP2
>::type map_type;
map_type x;
(where the test is made-up; here we're testing whether T is a pointer type or not)
If the condition cannot be known until runtime, then some form of dynamic polymorphism is needed. Typical instances of such polymorphism in C++ are subtyping, boost::variant or when push comes to shove, boost::any. Which one you should pick* and how you should apply it depends on your general design; we don't know enough.
*: very likely not to be boost::any.
You have a couple of choices. If C1 and C2 are both POD types, you could use a union, which allows access to the common initial sequence:
struct C1 {
// ....
};
struct C2 {
// ...
};
union TP {
C1 c1;
C2 c2;
};
union TP x;
std::cout << x.c1.code; // doesn't matter if `code` was written via c1 or c2.
Note that to keep the initial sequence "common", you really want to change the names so the second member (descr/other) has the same name in both versions of the struct.
If they're not PODs, you can use inheritance to give you a common type.
C++, however, doesn't have a direct counterpart to Python's famous "duck typing". While templates provide type erasure (to at least some degree), you'd end up with kind of the reverse of what you're doing in Python. Instead of the variation between the two types happening where you deal with the variable, you'd allow code to deal with two different types that had common syntax. This is different, however, in that it requires that the compiler be able to resolve the actual type being used with any particular template at compile time, not just run time.
If you really need to resolve the type at run time, then templates probably won't work -- you'll probably need to use a union or base class.
If you really need two different types, the best thing to do would be (assuming the classes are similar and has some similar member functions) to have an abstract class, say, CBase (see http://www.cplusplus.com/doc/tutorial/polymorphism/) and then define two subclasses C1 and C2 of this abstract class.
Now your code can be written as follows:
CBase *x;
if (condition) {
x = new C1();
} else {
x = new C2();
}
In case you can not abstract C1 and C2 into a common abstract class, well, then you'll need two different variables and condition acts like your flag using which you can know later which variable has been populated and which structure to work with.
Although there may be some ways to do it, they're mostly tricky and not maintainable, just as Damon mentioned.
I recommend you to use template function. What you really want is to access the same member/functions for different class. In template function, you can access the object of a "general type" as long as the type provides the operation you use in the template.
For example, in your case you can simply extract the common parts into a template function like this.
struct TP1
{
// common part
int i;
int j;
// different part
float f;
};
struct TP2
{
// common part
int i;
int j;
// different part
double d;
};
template<typename CType>
void f(CType a)
{
// as long as CType has variable i, j
cout << a.i << endl;
cout << a.j << endl;
}
int main(int argc, char* argv[])
{
bool choice;
// get a choice from console during runtime
cin >> choice;
if (choice)
{
TP1 x = {0, 0};
f(x);
}
else
{
TP2 x = {1, 1};
f(x);
}
return 0;
}
i think you can do it by runtime polymorphism.
class C_Base { /*all common variables*/ } ;
class C1 : public C_Base { ... };
class C2 : public C_Base { ... };
typedef map<long, C_Base *> TP;
{
...
TP x;
if (condition)
/*use x as if values are C1 * */
else
/*other way round*/
}
In order to use two different types through a common variable, the types
must have a common base class. Since what you have is two different
types which you can't change, and which don't have a common base class,
you need some sort of duck typing. In C++, only templates use duck
typing: one solution would be to move all of the code after the
condition into a separate function template, to which you pass the
results, and then write something like:
if ( condition_is_true )
wrapped_code( A() );
else
wrapped_code( B() );
Depending on the code that actually follows the condition, this may be
more or less convenient.
A more general alternative is to create your class hierarchy to wrap the
maps. This solution is a bit verbose, but very easy: just define a base
class with the interface you want, say:
class Map
{
public:
virtual ~Map() {}
virtual std::string getCode( long index ) const = 0;
virtual std::string getDescr( long index ) const = 0;
virtual int getX( long index ) const = 0;
};
, and then a template which derives from it:
template <typename T> // Constraint: T must have accessible members code, other and x
class ConcreteMap : public Map
{
std::map <long, T> myData;
public:
virtual std::string getCode( long index ) const
{
return myData[index].code;
}
virtual std::string getDescr( long index ) const
{
return myData[index].descr;
}
virtual int getX( long index ) const
{
return myData[index].x;
}
};
Your if then becomes:
std::unique_ptr<Map> x = (condition_is_true
? std::unique_ptr<Map>( new ConcreteMap<C1> )
: std::unique_ptr<Map>( new ConcreteMap<C2> ));
What you're trying to do is not possible in C++. Variables in C++ have a fixed type which is defined at compile time and they can't change type at run time. But C++ does provide polymorphism (which looks like dynamic types) which allows derived types to implement base class functionality, but the only way to access type specific methods is to have a type bound to the base class, if you have a type bound to the derived type then you can only call that type's implementation*:
class Base
{
public: virtual void Func () = 0;
};
class C1 : public Base
{
public: virtual void Func () {}
};
class C2 : public Base
{
public: virtual void Func () {}
};
void SomeFunc ()
{
C1 *c1 = new C1;
C2 *c2 = new C2;
Base *b;
b = c1;
b->Func (); // calls C1::Func
b = c2;
b->Func (); // calls C2::Func
}
It looks like b has changed type, but it's actual type has remained the same, it is always a Base * and it can only be assigned the value c1 and c2 because they share a common base class Base. It is possible to go the other way:
Base *b = new C1;
C1 *c1 = dynamic_cast <C1 *> (b);
but it requires the dynamic_cast and that requires something called RTTI (Run-Time Type Information) which provides the compiled code a way to check that b is actually pointing to a C1 type. If you were to do the following:
Base *b = new C2;
C1 *c1 = dynamic_cast <C1 *> (b);
c1 would be the null pointer, not b. But C1 and C2 must still have a common base class for this to work. This is not legal:
class Base {....}
class C1 : public Base {....}
class C2 {....} // not derived from Base!
Base *b = new C2; // no way to convert C2 to Base!
C2 *c2 = new C2;
b = dynamic_cast <Base *> (c2); // still won't work, C2 not a Base
b = new C1; // fine, C1 is a Base
C1 *c1 = new C1;
b = c1; // again, fine
c1 = dynamic_cast <C1 *> (b); // fine, C1 is a derived type of Base, this will work
c2 = dynamic_cast <C2 *> (b); // won't work, C2 is not a derived type of Base
If C1 and C2 are related (say, CSquare and CCircle) then a common base class makes sense. If they are not related (say, CRoad and CFood) then a common base class won't help (it can be done, but it's not very logical). Doing the former (common base class) has been well described in the other answers. If you need to do the latter, then you may need to re-think how the code is structured to allow you to do the former.
It would help if you could expand on what you want to do with x. Since x is a container, do you just want to do container related operations?
Of course, things are never that easy in C++ and there are many things that can confuse the issue. For example, a derived type may implement a public base class virtual method privately:
Example:
class B
{
public:
virtual void F () = 0;
};
class C : public B
{
private:
virtual void F () { .... }
};
C *c = new C;
B *b = c;
b->F (); // OK
c->F (); // error