I have a class Obj with zero or two arguments.
I would something like this:
class Sphere{
public:
int radius=1;
int a;
};
class Plane{
public:
int a=11;
int b=12;
int c=13;
};
class Light{
public:
int pos=555;
int a;
};
class Obj{
public:
std::string name;
Sphere s;
Light l;
Plane p;
int type;
bool defined;
//with no arguments
Obj(){
defined=false;
type=0;
}
//Type Sphere as second arg
Obj(std::string name,Sphere _s){
defined=true;
type=1;
s=_s;
}
//Or Type Light as second arg
Obj(std::string name,Light _l){
defined=true;
type=2;
l=_l;
}
//Or Type Plane as second arg
Obj(std::string name,Plane _p){
defined=true;
type=3;
p=_p;
}
};
The meaning should be the ability to put several objects of different types in the same array typed in only one way.
Is it possible in some way ? Any suggestion will be appreciated. Thanks
EDIT:
(C++11)
Knowing that it compiles is already a great start. Thanks.
The above example only works if the Sphere, Plane, Light classes have no arguments.
Adding the same argument number to the Sphere, Plane, Light classes also fails. The same if the 3 classes have different number of arguments.
#include <iostream>
class Sphere{
public:
int radius=1;
int a;
Sphere( int _a ){
a=_a;
}
};
class Plane{
public:
int a=11;
int b=12;
int c=13;
Plane( int _a ){
a=_a;
}
//Plane(int _a,int _b,int _c){
// a=_a;b=_b;c=_c;
//}
};
class Light{
public:
int pos=555;
int a;
Light(int _a){
a=_a;
}
};
class Obj{
public:
std::string name;
Sphere s;
Light l;
Plane p;
int type;
bool defined;
//with no arguments
Obj(){
defined=false;
type=0;
}
//Type Sphere as second arg
Obj(std::string _name,Sphere _s){
name=_name;
defined=true;
type=1;
s=_s;
}
//Or Type Light as second arg
Obj(std::string _name,Light _l){
name=_name;
defined=true;
type=2;
l=_l;
}
//Or Type Plane as second arg
Obj(std::string _name,Plane _p){
name = _name;
defined=true;
type=3;
p=_p;
}
};
using namespace std;
int main()
{
int a=111;
int b=222;
int c=333;
Obj sph1=Obj("sphere1",Sphere(a));
Obj sph2=Obj("sphere2",Sphere(b));
Obj lig1=Obj("Light1", Light(c));
Obj lig2=Obj("Light2", Light(c));
//Obj pla1=Obj("Plane1", Plane(a,b,c));
//Obj pla2=Obj("Plane2", Plane(c,b,a));
cout<<"sp1 name:" << sph1.name << endl;
cout<<"sp1 radius:"<< sph1.s.radius << endl;
//cout<<"pla1 name:" << pla1.name << endl;
//cout<<"pla1 p1:" << pla1.p.p1 << endl;
//cout<<"pla2 name:" << pla1.name << endl;
//cout<<"pla2 p1:" << pla1.p.p1 << endl;
cout<<"Hello"<<endl;
return 0;
}
Here the compiler error:
Error:
vt.cpp: In constructor ‘Obj::Obj()’:
vt.cpp:64:10: error: no matching function for call to ‘Sphere::Sphere()’
Obj(){
^
vt.cpp:22:5: note: candidate: Sphere::Sphere(int)
Sphere( int _a ){
^~~~~~
vt.cpp:22:5: note: candidate expects 1 argument, 0 provided
vt.cpp:18:7: note: candidate: constexpr Sphere::Sphere(const Sphere&)
class Sphere{
^~~~~~
The C++17 solution for this is std::variant
, although based on defined
you probably want to wrap it in an std::optional
.
class Obj{
public:
std::string name;
using Value = std::variant<Sphere, Light, Plane>;
std::optional<Value> v;
//with no arguments
Obj() = default;
//Type Sphere as second arg
Obj(std::string name,Sphere _s) : v{std::make_optional<Value>(_s)} { }
// ...
};
std::variant
keeps track of which type is stored for you, and you don't have to pay for the overhead of all three instances. std::optional
handles the defined
part for you.
You could be a bit slicker by templating the constructor and passing the second argument along blindly.
template <typename T>
Obj(std::string name, T _v) : v{std::make_optional<Value>(_v)} { }