I am trying to use auto in combination with a virtual function. Please consider the following code:
#include <iostream>
#include <vector>
struct foo {
virtual auto get() = 0;
};
template <typename T>
struct bar : foo
{
virtual T get() override {return new T;};
};
int main() {
std::vector<foo*> Vec;
Vec.push_back(new bar<decltype(1333)>);
Vec.push_back(new bar<decltype(3.14159)>);
Vec.push_back(new bar<decltype(true)>);
std::cout << Vec[0]->get() << std::endl;
std::cout << Vec[1]->get() << std::endl;
std::cout << Vec[2]->get() << std::endl;
}
You can see online at Godbolt that code does not compile. The compiler returns:
error: virtual function cannot have deduced return type
Please help me to fix the above code.
This will not work in C++. Vec[i]->get() must imply a return type at compile time. How to do something like this depends on your needs. Assuming you have a closed set of types you want to work with, you can use std::variant:
#include <iostream>
#include <variant>
#include <vector>
struct foo {
using ReturnType = std::variant<int, bool, double>;
virtual ReturnType get() = 0;
// you need a virtual destructor in all base classes
virtual ~foo() = default;
};
template <typename T>
struct bar : public foo {
using foo::ReturnType;
ReturnType get() override {
return T{};
};
};
int main() {
std::vector<foo*> Vec;
Vec.push_back(new bar<decltype(1333)>);
Vec.push_back(new bar<decltype(3.14159)>);
Vec.push_back(new bar<decltype(true)>);
auto const visiter = [](auto const elm) { std::cout << elm << '\n'; };
for (auto& elm : Vec) {
std::visit(visiter, elm->get());
delete elm; // don't leak memory
elm = nullptr;
}
}
Don't use raw-owning pointers in this context. They will lead to memory leaks.
First, see here: C++ virtual function return type
Your problem is here:
virtual auto get() = 0;
A virtual function needs to have a known return type, or how will the overrides be checked to ensure covariance of their return types?
In particular, you seem to want get() overrides which return int, double, and bool. But these types are not covariant with anything (they have no base class), so you can't do that, because there is no type you can put in place of auto that will actually work.
You could make a base class for the value types and then store wrappers derived from that:
struct BaseValue {};
struct IntValue : BaseValue {};
// etc
But then it's hard to see how you can do anything useful with the BaseValue* you'd get().
Related
This question already has answers here:
Inheritance in std::map with base class as value
(4 answers)
Closed last month.
I would like to have a function that accepts a std::map containing a (pointer to a) base class, but I can't get it to work. Here is my minimal and simplified example:
struct Entity {
int i;
Entity(const int i_) : i(i_) {}
virtual ~Entity() {}
};
struct A : public Entity {
bool a;
A(const int i_, const bool a_) : Entity(i_), a(a_) {}
};
void efunc(const std::map<int, std::shared_ptr<Entity>>& m) {
for (auto& it : m) {
std::cout << it.second->i << std::endl;
}
}
int main() {
std::map<int, std::shared_ptr<A>> aMap;
std::shared_ptr<A> myA = std::make_shared<A>(1, true);
aMap.insert({myA->i, myA});
// efunc(aMap); // DOES NOT WORK
}
Obviously simply passing the map this way is not allowed, but how can I make it work?
As a bonus, how would I detect the type in the map so that I can execute code specific to that subclass?
Thank you in advance!
Update: Using templates seems to do the trick, this is what I'm using right now:
template <class T>
void tfunc(const std::map<int, std::shared_ptr<T>>& m) {
for (auto& it : m) {
std::cout << it.second->i << std::endl;
}
}
It does feel a bit strange, since I loose IntelliSense and I don't understand why the compiler is not giving me an error. I feel like concepts might help (e.g. to make sure T inherits Entity), but this is beyond what I need right now.
Thank you again for all the responses!
Update 2: Alright, here it is using concepts to ensure that the template containst the member i:
template <class T>
concept hasI = requires(T a) { a.i; };
template <class T>
concept inheritsEntity = std::derived_from<T, Entity>;
template <hasI T>
/// alternative: template<inheritsEntity T>
void tfunc(const std::map<int, std::shared_ptr<T>>& m) {
for (auto& it : m) {
std::cout << it.second->i << std::endl;
}
}
To answer your first question, you could use template programming; the compiler will generate the code specific to the required type for you:
template <class EntityType>
void efunc_template(const std::map<int, std::shared_ptr<EntityType>>& m) {
for (auto& it : m) {
std::cout << it.second->i << std::endl;
}
}
The function can then be called in your main function using:
efunc_template<A>(aMap);
Regarding your second question (how to execute code specific to your type), check can be done at compile time using:
template <class EntityType>
void efunc_template(const std::map<int, std::shared_ptr<EntityType>>& m) {
for (auto& it : m) {
if constexpr (std::is_same_v<A, EntityType>) {
std::cout << "Type is A" << std::endl;
} else {
std::cout << "Other type: " << typeid(EntityType).name() << std::endl;
}
std::cout << it.second->i << std::endl;
}
}
You can make aMap a std::map<int, std::shared_ptr<Entity>> and still hold shared pointers to instances of A.
In the code below myA which will be stored in the map is of type std::shared_ptr<Entity> but is initialized with a shared pointer to an instance of class A. This is OK because A is derived from Entity.
//----------------------------VVVVVV--------
std::map<int, std::shared_ptr<Entity>> aMap;
//--------------VVVVVV-------------------------V-----------
std::shared_ptr<Entity> myA = std::make_shared<A>(1, true);
aMap.insert({ myA->i, myA });
efunc(aMap); // Works now.
As commented below, myA can also be std::shared_ptr<A> and still be stored in aMap:
std::shared_ptr<A> myA = std::make_shared<A>(1, true);
Alternatively, if using efunc is quite rare, you can keep aMap as std::map<int, std::shared_ptr<A>>.
Then whenever you need to call efunc, create a temporary std::map<int, std::shared_ptr<Entity>> containing a copy of the content of aMap and use it to call efunc.
The shared pointers will still refer to your original objects.
It's inefficient, but if you don't use efunc frequently it might be sufficient.
Change the declaration of your std::map variable from :
std::map<int, std::shared_ptr<A>> aMap;
to :
std::map<int, std::shared_ptr<Entity>> aMap;
This way, the map matches what's expected by your efunc function.
Using inheritance as you did, you can indeed insert in your aMap data structure instances of type Entity as well as instances whose the type derivates from Entity. This is the case for struct A.
To detect the type in the map, you can either use a scoped enum as member within the Entity struct, or using the typeid operator, which returns an instance of std::type_info, with which you can get the name of the type.
However, the returned name is not guaranteed to be the same across implementations.
======== output from Clang ========
myint has type: i
mystr has type: NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
======== output from MSVC ========
myint has type: int
mystr has type: class std::basic_string<char,struct > std::char_traits,class std::allocator >
For this reason, you might want to use dynamic_cast instead, such as :
class Base {
public:
virtual ~Base() {}
};
class T1Base : public Base {};
class T2Base : public Base {};
template <typename T> class T1 : public T1Base {};
template <typename T> class T2 : public T2Base {};
int main()
{
Base *b = find_by_name();
if (dynamic_cast<T1Base*>(b))
cout << "T1" << endl;
else if (dynamic_cast<T2Base*>(b))
cout << "T2" << endl;
else
cout << "unknown" << endl;
delete b;
return 0;
}
[Extensively edited for clarity and typos, and now with attempted solution, old version at the end]
New version of the question
I have a class which needs to store a functor. The problem is that the functor is an expression template (meaning that each functor is of different type). On top of that I need to collect all the objects of that class in a vector.
Below my attempts:
I do not write the code for the functors as expression templates as it is too long. I will therefore test the code by trying to store two functors of two different classes
struct FunctorTypeA {
double a;
FunctorTypeA(const double ap): a(ap) {}
double operator()(const double x){ return a;}
};
struct FunctorTypeB {
double a, b;
FunctorTypeB(const double ap, const double bp): a(ap), b(bp) {}
double operator()(const double x){ return a+b*x;}
};
I think, correct me if I am wrong, that if the code below works for the two functors above, it should work for any functor constructed with expression templates.
The first idea would be
template <typename functorType> struct myClass {
functorType functor;
myClass(functorType funct): functor(funct){}
};
I can store a functor inside an object of that class with:
FunctorTypeA functor1(1.2);
FunctorTypeB functor2(1.5, 5.0);
myClass myObj1(functor1);
myClass myObj2(functor2);
cout << myObj1.functor(0.2) << " " << myObj1.functor(0.2) << "\n\n";
However I cannot store those objects in an std::vector as they have different types.
Therefore I tried:
struct myClass2Base {
virtual ~TrialClassBase() = default;
virtual double functor(const double x) = 0;
};
template <typename functorType> struct myClass2 : public myClass2Base {
functorType functorStored;
myClass2(functorType funct): functorStored(funct){}
double functor(const double x){return functorStored(x);}
};
and I can construct a vector of those objects as
std::vector<myClass2Base*> vecOfObj;
vecOfObj.push_back(new myClass2(functor1));
cout << vecOfObj[0]->functor(0.3) << "\n\n";
This one works.
Is there a better solution?
Thanks!
Old version of the question
I have a class which needs to store a functor which is an expression template (meaning that each function is of different type). I also need to put the objects of that class in an std::vector.
My Attempt is
struct TrialClassBase{
virtual double get_functor(double) = 0;
};
template <typename derived> struct TrialClass : public TrialClassBase {
derived functor;
TrialClass(derived fun): functor(fun){}
double get_functor(double x) {return functor(x);}
};
std::vector<shared_ptr<TrialClassBase>> vecOfObjs;
Then I try to add an object to the vector. As an example I use an std::function, only as an example: the functor in my case will be an expression template.
std::function<double(double)> funcccc = [](double x){return x*x;};
vecc.emplace_back(TrialClass(funcccc));
cout << vecc[0]->get_functor(0.3) <<"\n\n";
This fails to compile. What mistake am I making? How can I do what I want to do?
Thanks!
I see you have improved your question.
Yes there are debatably better solutions.
One nice solution the std lib offers you, is to use type-erasure using std:function.
#include <vector>
#include <functional>
#include <iostream>
#include <cmath>
struct FunctorTypeA {
double a;
FunctorTypeA(const double ap): a(ap) {}
auto operator()([[maybe_unused]]const double x) const { return a;}
};
int main(){
auto funcVec{std::vector<std::function<double(double)>>{}};
funcVec.push_back(FunctorTypeA{3.14}); // functor allowed
funcVec.push_back([](double x){return x;}); // lambda allowed
funcVec.push_back(sqrt); // function pointer allowed, e.g. C math function
for(auto&& f:funcVec){
std::cout << f(2.0) << '\n';
}
}
This way you don't complicate things with inheritance and pointer casting. Easy to make mistakes there. std::vector and std::function will do all the cleaning up. (That was something you can easily miss in your vector-of-pointers solution)
Note: std::function may perform a dynamic allocation. But so does std::vector and inheritance has vtable overhead.
Your construction of the shared_ptr is invalid
As pointed in the comments:
A base class should always have a virtual destructor
push_back is to be preferred
struct TrialClassBase {
virtual double get_functor(double) = 0;
virtual ~TrialClassBase() = default;
};
template <typename derived> struct TrialClass : public TrialClassBase {
derived functor;
TrialClass(derived fun) : functor(fun) {
}
double get_functor(double x) {
return functor(x);
}
};
std::vector<std::shared_ptr<TrialClassBase>> vecc;
int main() {
std::function<double(double)> funcccc = [](double x) { return x * x; };
vecc.push_back(
std::make_shared<TrialClass<std::function<double(double)>>>(funcccc));
std::cout << vecc[0]->get_functor(0.3) << "\n\n";
}
I would like to have a unique_ptr class member that points to the base class, but later in the constructor through polymorphism can be changed to point to a sister class that also derives from the same base class.
While I don't get any errors in the constructor setting this polymorphism, it does not seem to work correctly, since I get error messages that my polymorphic pointer can't find a member of the sister class to which I thought the pointer was now pointing.
How do I correctly achieve polymorphism here?
class A {
int bar;
};
class B : public A {
int foo;
};
class C: public A {
C();
std::unique_ptr<A> _ptr; // changing to std::unique_ptr<B> _ptr removes the "class A has no member 'foo'" error
};
C::C() : A()
{
_ptr = std::make_unique<B>(); // no errors here
int w = _ptr->foo; // class A has no member 'foo'
}
When you assign
_ptr = std::make_unique<B>();
This works because B is a derived class of A, however _ptr is still a unique_ptr to the base class. You can't change the type of a variable after it's declared.
So what are your options?
Because you know that _ptr stores a pointer to the derived class B, you can do a cast after dereferencing it:
_ptr = std::make_unique<B>();
// derefence the pointer, and cast the reference to `B&`.
B& reference_to_sister = (B&)(*_ptr);
int w = reference_to_sister.foo;
If you take this approach, you'll have to somehow keep track of which derived class is in _ptr, or you'll run the risk of running into bugs.
Alternatively, if you're using C++17, you can use std::variant:
class C : public A {
void initialize(A& a) {
// Do stuff if it's the base class
}
void initialize(B& b) {
// Do different stuff if it's derived
int w = b.foo;
}
C() {
_ptr = std::make_unique<B>(); // This works
// This takes the pointer, and calls 'initialize'
auto initialize_func = [&](auto& ptr) { initialize(*ptr); };
// This will call 'initialize(A&)' if it contains A,
// and it'll call 'initialize(B&)' if it contains B
std::visit(initialize_func, _ptr);
}
std::variant<std::unique_ptr<A>, std::unique_ptr<B>> _ptr;
};
In fact, if you use std::variant this will work even if A and B are completely unrelated classes.
Here's another short variant example
#include <variant>
#include <string>
#include <iostream>
void print(std::string& s) {
std::cout << "String: " << s << '\n';
}
void print(int i) {
std::cout << "Int: " << i << '\n';
}
void print_either(std::variant<std::string, int>& v) {
// This calls `print(std::string&) if v contained a string
// And it calls `print(int)` if v contained an int
std::visit([](auto& val) { print(val); }, v);
}
int main() {
// v is empty right now
std::variant<std::string, int> v;
// Put a string in v:
v = std::string("Hello, world");
print_either(v); //Prints "String: Hello, world"
// Put an int in v:
v = 13;
print_either(v); //Prints "Int: 13"
}
#include <iostream>
template<typename Impl>
struct renderer{
void get(){
static_cast<Impl*>(this)->get();
}
};
struct open_gl : public renderer<open_gl>{
void get(){
std::cout << "OpenGL" << std::endl;
}
};
struct direct_draw : public renderer<direct_draw>{
void get(){
std::cout << "DX" << std::endl;
}
};
template<typename T>
void print_renderer(renderer<T> r){
r.get();
}
int main() {
auto gl = open_gl();
auto dx = direct_draw();
print_renderer(gl);
print_renderer(dx);
}
Why can't I change the parameter of print_renderer to void
print_renderer(const renderer<T> &r)?
cannot convert 'this' pointer from 'const renderer<open_gl>' to 'renderer<open_gl> &'
`
Why do I get a runtime error when I rename the method get in open_gl from
get to get1? Shouldn't this trigger a compiler error? Error = Stack overflow
**Note I am using the latest MSVC
1) Because get is not a const member function : it cannot make the promise of not modify your (const) argument.
You could declare get as const, and it compiles fine :
void get() const { ... }
2) The base get method will be called, going into infinite recursion : Stack Overflow.
If you declare your function override (it needs to be virtual), the compiler will throw an error if it does not indeed override a base method :
void get1() override { ... } // Compiler error
void get() override { ... } // Ok
Note:
The title is "Static polymorphism in C++", but I think that you misunderstood what is static polymorphism : it does not (have to) make use of inheritance (as you did). Rather, the templates compile-time duck typing will statically "resolve" function calls for you.
That is, you don't need related types, you don't need the base renderer class at all, and you can simply do the following (in which case, renaming to get1 will cause a compiler error) :
#include <iostream>
struct open_gl {
void get(){
std::cout << "OpenGL" << std::endl;
}
};
struct direct_draw {
void get(){
std::cout << "DX" << std::endl;
}
};
template<typename T>
void print_renderer(T r){
r.get();
}
int main() {
auto gl = open_gl();
auto dx = direct_draw();
print_renderer(gl);
print_renderer(dx);
}
Live demo
Becuase get is not marked const.
Because the base class method is used (irrelevantly of cast), and it goes into infinite loop.
Consider the following class structure:-
class foo {
public:
int fun () {
cout << "in foo" << endl;
}
};
class bar_class1:public foo {
public:
int fun () {
cout << "in bar_class1" << endl;
}
};
class bar_class2:public foo {
public:
float fun () {
cout << "in bar_class2" << endl;
}
};
main () {
foo * foo_pointer = new bar_class1();
foo_pointer->fun();
}
The output of the above program is in foo. Is there a way, that using a pointer of type foo * which actually points to an object of type bar_class1 or bar_class2, we can call the fun function of the derived class instead of the base class? I am not able to make the fun function virtual in the base class foo since, then there is a return type conflict for function foo in the derived class bar_class2.
Here's my comments as an answer.
You cannot do that.
If that kind of polymorphism were possible, wouldn't that break horribly when code calls foo::fun (expecting an int) on an object whose actual type is bar_class2 and thus gets a float? Do you want to simply throw away type safety?
If you want different return types, sounds like you want a template. But you cannot use templates quite in the way that you want to use foo(). Static polymorphism (templates) and run time polymorphism (late binding) don't mix well. You need to redesign your oop structure.
If you absolutely hate type safety, you can sort of do this with void pointers. But for the love of Flying Spaghetti Monster, don't ever do this in c++. Please close your eyes before reading the following code to avoid exposure.
#include <iostream>
class foo {
public:
virtual void* fun() = 0;
virtual ~foo(){};
};
class bar_class1: public foo {
public:
void* fun() {
return &value;
}
private:
int value = 1;
};
class bar_class2: public foo {
public:
void* fun() {
return &value;
}
private:
float value = 1.1;
};
int main() {
foo* foo_pointer1 = new bar_class1();
foo* foo_pointer2 = new bar_class2();
// in c++ compiler must know the type of all objects during compilation
std::cout << *reinterpret_cast<int*>(foo_pointer1->fun()) << '\n';
std::cout << *reinterpret_cast<float*>(foo_pointer2->fun()) << '\n';
delete foo_pointer1;
delete foo_pointer2;
}
Perhaps similar to the existing answer, I really hope you realize changing your design is better than this mess, but I believe it's the best you're going to get. I force you to specify the return type at the callsite (e.g., someFoo->fun<int>()), since you're going to have to know it anyway, and dispatch based on that. Any funny business and you'll get an exception. Also keep in mind the performance of this is, I imagine, less than desirable.
#include <cassert>
#include <stdexcept>
#include <type_traits>
struct foo {
virtual ~foo() = default;
template<typename T, typename = typename std::enable_if<std::is_same<T, int>::value>::type, typename = void>
T fun();
template<typename T, typename = typename std::enable_if<std::is_same<T, float>::value>::type>
T fun();
};
struct bar_class1 : foo {
int fun() {
return 2;
}
};
struct bar_class2 : foo {
float fun() {
return 3.5f;
}
};
template<typename T, typename, typename Dummy>
T foo::fun() {
if (auto *p = dynamic_cast<bar_class1 *>(this)) {
return p->fun();
} else if (dynamic_cast<bar_class2 *>(this)) {
throw std::invalid_argument("Mismatching dynamic type.");
} else {
return 1;
}
}
template<typename T, typename>
T foo::fun() {
auto *p = dynamic_cast<bar_class2 *>(this);
if (dynamic_cast<bar_class1 *>(this) || !p) {
throw std::invalid_argument("Mismatching dynamic type.");
} else if (auto *p = dynamic_cast<bar_class2 *>(this)) {
return p->fun();
}
assert(false); //should never get here, but compiler doesn't know that
}
If you'd like the main function, I've written a complete sample.
To answer your question: No, late binding isn't possible without deciding the return type. ...at least not in a reasonable manner, see user2079303's great counter-example. But...
you may change your code (for example) into something like the following, using the keyword virtual and equalize the return type for instance to void:
class foo
{
public:
virtual void fun(std::ostream& out) {
out << "in foo" << std::endl;
}
};
so you can decide the output type later:
class intFoo: public foo
{
public:
void fun(std::ostream& out) {
// output an int
out << "in bar_class1. data: " << data << endl;
}
int data;
};
class floatFoo: public foo
{
public:
void fun(std::ostream& out) {
// output a float
out << "in bar_class2. data: " << data << endl;
}
float data;
};
For brevity, I double-use the output stream - now a parameter of the function fun() - function to demonstrate type-dependent portion of your derived class. In your application, the parameter will probably be of another, more useful type.
The function fun is not a virtual function since you didn't use the keyword "virtual" to decorate it. So, the compile will determine which function to call at compiling time. So, there is no way to tell the compiler to call another function because the compiler will use its static type, i.e. the variable definition type -- foo *.