C++ How to conditionally initialize a template class object? - c++

I have a template class
template <typename T>
class myClass
{
T variable;
....
};
I want to create an object of myClass , depending of some condition the object should either hold a string or int . how do I conditionally initialize like below :
if (condition1)
myClass<userDefinedType1> x;
else
myClass<userDefinedType2> x;
I don't think the code I wrote above is possible , but the problem is whether the type should be userDefinedType1 or userDefinedType2 is not known to me at compile time . How can I initialize the template class object when the type will only be known to me at run time based on user inputs ?

If the type of the problem isn't known until runtime, then your options are limited.
If the number of possible instantiations is small/simple and you are using c++17 or above, one option is to just use std::variant:
using MyClass = std::variant<std::monostate, myClass<userDefinedType1>,myClass<userDefinedType2>>;
auto v = MyClass{}; // create one without a value to start
if (condition) {
v = myClass<userDefinedType1>{};
} else {
v = myClass<userDefinedType2>{};
}
std::variant takes care of ensuring incorrect access does not occur (which prevents strict-aliasing violations), and it also will ensure that non-trivial destructors get invoked for the correctly instantiated types. This can then be used easily with visit assuming the functions that need to be called are homogeneously defined, since you can use a lambda with an auto parameter which will choose the correct type:
std::visit([](auto& cls){
cls.do_something();
}, v);
One other utility that can be used is std::any, for a similar purpose:
auto v = std::any{};
if (condition) {
v = myClass<userDefinedType1>{};
} else {
v = myClass<userDefinedType2>{};
}
The downside with std::any is that accessing the internal types is much more complicated and needs to be polled with std::any_cast. Still doable, but not so nice
Alternative, polymorphism is an option if your template might have some base-class that you can erase to. This may not work for every design, but in the case that it does you can simply use a std::unique_ptr to hold an instance to the base while you construct the class.
class myClassBase {
...
virtual auto common_api(...) -> void = 0;
...
};
template <typename T>
class myClass : public myClassBase {
...
auto common_api(...) -> void override { ... }
...
};
...
auto v = std::unique_ptr<Base>{};
if (condition) {
v = std::make_unique<myClass<userDefinedType1>>(...);
} else {
v = std::make_unique<myClass<userDefinedType2>>(...);
}
The last real option is to perform some form of manual type-erasure, where you create a wrapper class that erases the template argument. This solution is not generic and will be highly-specific to the class itself for how it can be erased. This may involve something as simple as a polymorphic wrapper that erases the type in the implementation, or it can be more complex such as bound function-pointers that do more work.

Ah, the crazy world of template metaprogramming (TMP). It's a lot easier now due to constexpr etc. but for programming the members of a class you still need to go old-school.
You have to generate your member by inheriting it from a base class.
That base class can be a template, including a fancy conditional TMP construct. You can see this in some of the standard library classes.
Hmm, you want the same data member (x), just having a different type. Well, that's easier: Make it of type T2. That can be generated using template stuff. Something like this:
using T2 = std::conditional_t<condition1, userDefinedType1, userDefinedType2>;
myClass<T2> x;
not known to me at compile time .
Well, that's another story. You can't change the generated code except at compile time.
Do you want a variable x that can hold either of two types?
That can use Object Oriented techniques of having a common polymorphic base class with different derived types.
Or, you can use a std::variant.
A (primitive) union as Ozger's answer shows is basically a low-level unchecked version of the same idea. In older versions of C++ such a union could not hold types that had constructors and destructors, but now they can, though you take on a lot of low-level effort. It's basically the language back-end to make writing variant simpler.

You can use a union.
union U {
myClass<userDefinedType1> a;
myClass<userDefinedType2> b;
};
U u;
if (condition1)
u.a = myClass<userDefinedType1>();
else
u.b = myClass<userDefinedType2>();
As stated in the comments, this compiles only if myClass has a trivial destructor, including the implicit destructor that will destruct T in myClass.

Unless you need to store x for future usage,
you can make the code which applies to x template too, and then you branch looks like:
template <typename T> void foo(T);
// ...
if (condition1) {
myClass<userDefinedType1> x;
foo(x);
} else {
myClass<userDefinedType2> x;
foo(x);
}
You can even move x in the function template, so code would be:
template <typename T> void bar(T)
{
myClass<T> x;
// ...
}
// ...
if (condition1) {
bar<userDefinedType1>();
} else {
bar<userDefinedType2>();
}
If you need to store x for future usage, you need either to use std::variant, polymorphism base class or type erasure.

Can't we just make class object dynamically. Something like this:
if (condition1)
myClass<userDefinedType1>* x = new myclass<userDefined1>;
else
myClass<userDefinedType2>* x = new myclass<userDefined2>;
Please let me know what's wrong with this approach

Related

Auto is not allowed here for lambda class in header file

I'm using c++14, auto is working fine on .cpp file, however when i try to create generic lambda in my class it generates error "Auto is not allowed here"
What did i do wrong?
Code:
class MyClass{
private:
// Works
std::function<void(MyClass&)> assign = [&](MyClass& other){
//do something
};
// Does not work
std::function<void(auto)> assign = [&](auto other){
//do something
};
// Does not work
auto assign = [&](auto other){
//do something
};
};
Your two attempts fail for different reasons. Let's look at the second one first:
auto assign = [&](auto other){
//do something
};
This could work in theory: the lambda expression has a definite type that could be deduced and used. However, as addressed in this Q&A (for C++11 but still relevant), the language simply does not allow auto member variables, so that's that.
Your first attempt fails for a more design-related reason. Well, the immediate issue is that void(auto) is not a valid type (auto is a syntactic keyword with tailored semantics, not a type), but one could conceivably use some tag types to make std::function<void(auto_t)> a valid specialization.
The issue, however, is that std::function is a type-erasure tool: its goal is to take in and conceal a functionoid (such as your lambda), then call it on demand. By the time the call is made, the functionoid's type is not known anymore, much less its potential operator() template -- handling the general case would require instantiating that template at runtime for a yet-unknown argument type, which is not possible.
Bottom line: storing the lambda is conceptually valid but its type deduction is not supported, and type erasure is the wrong tool for the job since templates are involved. Your only solution is to create an explicit functor class, and use that as a member:
class MyClass {
struct {
template <class T>
void operator()(T other) const {
// do something
}
} assign;
};
Bonus point: C++20 allows lambdas in unevaluated context and default-constructible lambdas, which means that the following insanity is now valid 🙃
class MyClass {
decltype([](auto other) {
// do something
}) assign;
};
These statements are perfectly ok on a different context / scope:
void myfunc() {
auto lambda = []() { }; // works fine
}
But the here you refer is a class definition, where there can't be members without explicit types.
class MyClass {
std::function<void(MyClass&)> assign = lambda;
// works, because there exists now a variable assign,
// of specific type, which is initialised from a lambda
auto kOne = 1; // fails for the same reason
};

Switching between two typedef structs, depending on boolean

I have a class with different functions. This class uses two typedef structs.
typedef struct {
struct head;
struct data1;
} data_struct_1;
typedef struct {
struct head;
struct data2;
} data_struct_2;
If a bool is true the data_struct_2 should be used, otherwise data_struct_1. The boolean is passed, when the object is created and stored as a membervariable in the constructor.
There are now different functions and declarations like
int calc_mean(data_struct_1 ds, int a){ ... }
or
data_struct_1 ds;
ds.data1 = 5;
Is there an elegant way, which allows me to switch between those two structs without implementing dozens of if-cases and duplicate all the relevant functions with the corresponding struct in the header?
First I was thinking about inheritance and overwritting relevant functions if bool is true.. but the header in the functions differs, so this wouldnt work
Maybe there is something like a placeholder or template, but i'm pretty new to c++
If boolean is compile-time value, std::conditional might be used:
template <boolean B>
using my_type = std::conditional_t<B, data_struct_1, data_struct_2>;
but it seems the boolean is runtime value, so I would use std::variant and std::visit:
class C
{
using Data = std::variant<data_struct_1, data_struct_2>;
Data m_data;
public:
C(bool b) : m_data{b ? Data{data_struct_1{}} : Data{data_struct_2{}}} {}
auto mean() const {
return std::visit([](const auto& data){ return calc_mean(data, 42); }, m_data);
}
};
You have a couple of options:
If the types are conceptually the same type of thing (i.e. they have the same interface and could be substituted for one another), you can:
Create an abstract base type and use polymorphism to change which concrete type is instantiated
Change all places that use these types into templates, change which template(s) are instantiated at runtime, and then rely on compile-time "duck-typing" to do the rest
If the types are conceptually different (i.e. they should not have the same interface), you can use a union type (union, std::variant).
Take a look at std::variant.
A variant can hold a number of alternative types, like this:
std::variant<data_struct_1, data_struct_2> m;
The member m can contain either a data_struct_1 or a data_struct_2. The variant will remember which, so there's no need for you to carry that bool around anymore.
The interesting question is now, how do you treat m in your functions? After all, it could be either a data_struct_1 or a data_struct_2 in there. Instead of using branches for distinguishing these cases, variant uses visit: Whenever you encounter a point in the function that depends on the concrete type stored in m, you move that code into two overloaded functions: One function that handles the data_struct_1 case and another that handles the data_struct_2 case. The example on the visit cppreference page shows some possibilities for doing that with varying degrees of syntactic overhead.
The good thing about this approach is that it forces you to always handle both cases properly. You can't just go ahead and write an implementation for one case and then forget about the other, having it blow up later at runtime. It also encourages a style where you clearly separate common parts of your code from type-specific parts, which often results in a cleaner design in the long term.
One possibility is to use std::variant:
typedef std::variant<data_struct_1, data_struct_2> data_struct_1_or_2;
void foo(data_struct_1_or_2 ds) {
if (auto ds1 = std::get_if<data_struct_1>(&ds)) {
// use ds1, which is type data_struct_1*
} else if (auto ds2 = std::get_if<data_struct_2>(&ds)) {
// use d2, which is type data_struct_2*
} else {
throw std::logic_error("Unexpected value in variant");
}
}
void bar(data_struct_1_or_2 ds) {
foo(ds); // Don't need to use an if statement here
}
If you have a data_struct_1 or data_struct_2 and want to pass a reference to the object, rather than a copy, you could use pointers in the std::variant instead (although it'll get a bit confusing with std::get_if because you'll end up with a pointer to a pointer):
typedef std::variant<data_struct_1*, data_struct_2*> data_struct_1_or_2_ptr;
A simple C solution would be a union with a bool tag:
typedef struct {
struct head;
bool type_flag;
union{
struct data1 d1;
struct data2 d2
};
} data_struct;
std::variant is an overkill for this case IMO. Inheritance and runtime polymorphism is a waste of runtime and memory.
Regards,
FM.

Invalid use of auto when declaring a variable C++, C++11

Whenever I try to compile my code I get an invalid use of auto. Coming from a Java background, I am not sure if I quite understand C++ templates. It seems like the compiler can infer the type. I am limited to C++11.
The error occurs in the GeneralRenderer.
Animal classes
class Animal {};
class Dog : public Animal {};
class Cat : public Animal {};
enum AnimalType {
Dog,
Cat
};
Render Classes
template<class T>
class AnimalRenderer {
virtual void draw(T entity) = 0;
};
class DogRenderer : public AnimalRenderer<Dog> {};
class CatRenderer : public AnimalRenderer<Cat> {};
Logic
class GeneralRenderer {
public:
void draw(
std::map<AnimalType, std::vector<Animal>> animalMap) {
for (auto entry: animalMap) {
AnimalRenderer<auto> renderer; // <--- Causes the error
switch (entry.first) {
case Dog:
renderer = DogRenderer();
break;
case Cat:
renderer = CatRenderer();
break;
}
for (auto animal: entry.second) {
renderer.draw(entry);
}
}
};
};
Informally speaking, auto can be used to declare and initialise a type at the same time. Then the compiler can infer the type from the value used to initialise the variable.
But that's not the case here; a statement like
auto n;
will fail similarly.
Note that auto merely stands in for a type that is known at compile time. It's there to make code more readable although its use is occasionally obligatory such as when working with lambda functions. It is not a variant-type construct cf. std::variant say of C++17.
Finally note that a std::map is a very different beast to a java.util.Map. There's no type erasure in C++ so a std::map<A, B> is a fundamentally different type to a std::map<C, D> if A is a different type to C and / or B differs from D. Furthermore, C++ allows you to store value types in maps.
As the other users already say, auto can‘t work without a righthand variable.
I think you should remove the template annotation from AnimalRenderer, then you can use the classes normally.
To get the result you want you effectively need to move the second for-loop inside the cases, where the deduction can be resolved for each case.
You don't want code duplication, so ideally you want a template function you can call to do the second loop. On the latest c++ standards you might also be able to declare a template lambda function.
That said, architecturally, I would suggest adding an interface base class for AnimalRenderer, and then you can declare a simple pointer to that base class, especially as you have already declared draw as virtual.

Why are template (non-static) member variables not supported in C++?

While static member variables can be templated in C++14 this wont work:
class SomeClass
{
public:
template<typename T>
T var = {};
};
int main()
{
SomeClass instance;
instance.var<int> = 50;
instance.var<double> = 0.1;
}
What are the reasons, that templates for variable members are not supported by the C++ standard since it should be possible in principle?
When you instantiate the class you don't know how much memory it will use. Does this class contain an int and a double? What if you write
instance.var<float> = 0.2;
instance.var<long long> = 1;
later in your code
This would make two objects of the same type SomeClass different, rendering the class concept as we understand it in c++ useless.
Also your code sample implies that var could change type during runtime, this can be done using std::variant or std::any.
It cannot be possible in principle or in practice, as the other answers explain: sizeof(SomeClass) would be impossible to compute in general, and SomeClass would no longer have any predictable or sane identity, defeating the purpose of its existence.
If there are only a select few types you wish to choose from, and you wish to change the "selected" type at runtime, perhaps a variant is what you're looking for?
#include <variant>
class SomeClass
{
public:
std::variant<int, double> var = {};
};
int main()
{
SomeClass instance;
instance.var = 50;
instance.var = 0.1;
}
(This requires C++17, but a Boost equivalent has been available for many, many years.)
It works because var will be as big as it needs to to store either an int or a double (plus some housekeeping), and this size is fixed no matter which "mode" your variant is in at any given time.
If you want to accept any type, you could use std::any, which is like a variant on drugs. The overhead is a little heavier, but if your requirements are really so relaxed then this can do the job.
But if you want multiple variables, have multiple variables.
c++ has value types with known sizes. All complete types in C++ that you can create can have their sizes calculated by the compiler based only on information at or above the line of creation within that compilation unit.
In order to do what you want, either the size of instances of a class varies with every template variable ever used in any compilation unit, or the size of instances varies over time as new elements are added.
Now you can create new data based on type, but it won't be inside the class; instead, you add a map storing the data.
using upvoid=std::unique_ptr<void, void(*)()>;
template<class T>
static upvoid make(){
return { new T, [](void*ptr){ delete static_cast<T*>(ptr); } };
}
std::map<std::type_index, upvoid> m_members;
template<class T>
T& get() {
auto it = m_members.find(typeid(T));
if (it == m_members.end()){
auto r = m_members.insert( {typeid(T), make<T>()} );
it=r.first;
}
return *it.second;
}
now foo.get<int>() allocates an int if it wasn't there, and if it was there gets it. Extra work would have to be done if you want to be able to copy instances.
This kind of mess emulates what you want, but its abstraction leaks (you can tell itmisn't a member variable). And it isn't really a template member variable, it just acts a bit like one.
Barring doing something like this, what you ask for is impossoble. And doing this as part of the language would be, quite frankly, a bad idea.

C++ One std::vector containing template class of multiple types

I need to store multiple types of a template class in a single vector.
Eg, for:
template <typename T>
class templateClass{
bool someFunction();
};
I need one vector that will store all of:
templateClass<int> t1;
templateClass<char> t2;
templateClass<std::string> t3;
etc
As far as I know this is not possible, if it is could someone say how?
If it isn't possible could someone explain how to make the following work?
As a work around I tried to use a base, non template class and inherit the template class from it.
class templateInterface{
virtual bool someFunction() = 0;
};
template <typename T>
class templateClass : public templateInterface{
bool someFunction();
};
I then created a vector to store the base "templateInterface" class:
std::vector<templateInterface> v;
templateClass<int> t;
v.push_back(t);
This produced the following error:
error: cannot allocate an object of abstract type 'templateInterface'
note: because the following virtual functions are pure within 'templateInterface'
note: virtual bool templateInterface::someFunction()
To fix this error I made the function in templateInterface not a pure virtual by providing a function body, this compiled but when calling the function the overide is not used, but instead the body in the virtual function.
Eg:
class templateInterface{
virtual bool someFunction() {return true;}
};
template <typename T>
class templateClass : public templateInterface{
bool someFunction() {return false;}
};
std::vector<templateInterface> v;
templateClass<int> i;
v.push_back(i);
v[0].someFunction(); //This returns true, and does not use the code in the 'templateClass' function body
Is there any way to fix this so that the overridden function is used, or is there another workaround to store multiple template types in a single vector?
Why your code doesn't work:
Calling a virtual function on a value doesn't use polymorphism. It calls the function which is defined for the type of this exact symbol as seen by the compiler, not the runtime type. When you insert sub types into a vector of the base type, your values will be converted into the base type ("type slicing"), which is not what you want. Calling functions on them will now call the function as defined for the base type, since not it is of that type.
How to fix this?
The same problem can be reproduced with this code snippet:
templateInterface x = templateClass<int>(); // Type slicing takes place!
x.someFunction(); // -> templateInterface::someFunction() is called!
Polymorphism only works on a pointer or reference type. It will then use the runtime type of the object behind the pointer / reference to decide which implementation to call (by using it's vtable).
Converting pointers is totally "safe" with regard to type slicing. Your actual values won't be converted at all and polymorphism will work as expected.
Example, analogous to the code snippet above:
templateInterface *x = new templateClass<int>(); // No type slicing takes place
x->someFunction(); // -> templateClass<int>::someFunction() is called!
delete x; // Don't forget to destroy your objects.
What about vectors?
So you have to adopt these changes in your code. You can simply store pointers to actual types in the vector, instead of storing the values directly.
When working with pointers you also have to care about deleting your allocated objects. For this you can use smart pointers which care about deletion automatically. unique_ptr is one such smart pointer type. It deletes the pointee whenever it goes out of scope ("unique ownership" - the scope being the owner). Assuming the lifetime of your objects is bound to the scope this is what you should use:
std::vector<std::unique_ptr<templateInterface>> v;
templateClass<int> *i = new templateClass<int>(); // create new object
v.push_back(std::unique_ptr<templateInterface>(i)); // put it in the vector
v.emplace_back(new templateClass<int>()); // "direct" alternative
Then, call a virtual function on one of these elements with the following syntax:
v[0]->someFunction();
Make sure you make all functions virtual which should be possible to be overridden by subclasses. Otherwise their overridden version will not be called. But since you already introduced an "interface", I'm sure you are working with abstract functions.
Alternative approaches:
Alternative ways to do what you want is to use a variant type in the vector. There are some implementations of variant types, the Boost.Variant being a very popular one. This approach is especially nice if you don't have a type hierarchy (for example when you store primitive types). You would then use a vector type like std::vector<boost::variant<int, char, bool>>
Polymorphism only works through pointers or references. You'll
need the non-template base. Beyond that, you'll need to decide
where the actual objects in container will live. If they're all
static objects (with sufficient lifetime), just using
a std::vector<TemplateInterface*>, and inserting with
v.push_back(&t1);, etc., should do the trick. Otherwise,
you'll probably want to support cloning, and keep clones in the
vector: preferably with Boost pointer containers, but
std::shared_ptr can be used as well.
The solutions given so far are fine though be aware that in case you were returning the template type other than bool in your example , none of these would help as the vtable slots would not be able to be measured before hand. There are actually limits , from a design point of view , for using a template oriented polymorphic solution.
Solution nr. 1
This solution inspired by Sean Parent's C++ Seasoning talk. I highly recommend to check it out on youtube. My solution simplified a bit and the key is to store object in method itself.
One method only
Create a class that will invoke method of stored object.
struct object {
template <class T>
object(T t)
: someFunction([t = std::move(t)]() { return t.someFunction(); })
{ }
std::function<bool()> someFunction;
};
Then use it like this
std::vector<object> v;
// Add classes that has 'bool someFunction()' method
v.emplace_back(someClass());
v.emplace_back(someOtherClass());
// Test our vector
for (auto& x : v)
std::cout << x.someFunction() << std::endl;
Several methods
For several methods use shared pointer to share object between methods
struct object {
template <class T>
object(T&& t) {
auto ptr = std::make_shared<std::remove_reference_t<T>>(std::forward<T>(t));
someFunction = [ptr]() { return ptr->someFunction(); };
someOtherFunction = [ptr](int x) { ptr->someOtherFunction(x); };
}
std::function<bool()> someFunction;
std::function<void(int)> someOtherFunction;
};
Other types
Primitive types (such as int, float, const char*) or classes (std::string etc.) may be wrapped in the same way as object class do but behave differently. For example:
struct otherType {
template <class T>
otherType(T t)
: someFunction([t = std::move(t)]() {
// Return something different
return true;
})
{ }
std::function<bool()> someFunction;
};
So now it is possible to add types that does not have someFunction method.
v.emplace_back(otherType(17)); // Adding an int
v.emplace_back(otherType("test")); // A string
Solution nr. 2
After some thoughts what we basically done in first solution is created array of callable functions. So why not just do the following instead.
// Example class with method we want to put in array
struct myclass {
void draw() const {
std::cout << "myclass" << std::endl;
}
};
// All other type's behaviour
template <class T>
void draw(const T& x) {
std::cout << typeid(T).name() << ": " << x << std::endl;
}
int main()
{
myclass x;
int y = 17;
std::vector<std::function<void()>> v;
v.emplace_back(std::bind(&myclass::draw, &x));
v.emplace_back(std::bind(draw<int>, y));
for (auto& fn : v)
fn();
}
Conclusion
Solution nr. 1 is definitely an interesting method that does not require inheritance nor virtual functions. And can be used to other stuff where you need to store a template argument to be used later.
Solution nr. 2, on the other hand, is simpler, more flexible and probably a better choice here.
If you're looking at a container to store multiple types, then you should explore boost variant from the popular boost library.