1

I have three classes:

class ShapeEditor {}
class PointEditor: ShapeEditor{}
class RectEditor: ShapeEditor{}

I want to define a function pointer in Manager class and pass it in constructor as lambda function. It looks like this:

class Manager {
public:
  ShapeEditor (*factory)();
  Manager(ShapeEditor (*_factory)());
}

Then I'm writing in main:

Manager m = Manager([] { return PointEditor(); }

and getting an error message that there is no constructor for this expression.

When I changes ShapeEditor to PointEditor in Manager - it works fine, but I need to specify different types in function result.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • If you do `return PointEditor()` in your lambda, is it a function that returns a `ShapeEditor`? – NathanOliver Nov 10 '20 at 15:41
  • 2
    Beware of returning derived types by value as base types. You will encounter [Object Slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). – François Andrieux Nov 10 '20 at 15:42
  • 2
    Note that class definitions end with a semicolon. Also note that by default class inheritance is `private`. – François Andrieux Nov 10 '20 at 15:44
  • @NathanOliver, If I correctly understand your question: PointEditor is derived from ShapeEditor. If I define a method in class like ShapeEditor foo() { return PointEditor()} - it will work. – Yehor Bublyk Nov 10 '20 at 15:49
  • @FrançoisAndrieux I know, thank you, but this is not relevant to the subject of the question, so I have simplified the code in the example – Yehor Bublyk Nov 10 '20 at 15:50
  • Yes, but you didn't specify a return type for the lambda, so it gets it from the returned object, and that object doesn't have the same type as the return type your function pointer needs – NathanOliver Nov 10 '20 at 15:50
  • `PointEditor(*)()` is unrelated to `ShapeEditor(*)()` and can't be converted to it. A much smaller example: `struct A {}; struct B : A{}; B f(); A (*g)() = f;`. – molbdnilo Nov 10 '20 at 15:51
  • @GeorgeBublyk It is relevant to the question. When code contains syntax errors like these, it is not clear what error is related to what you are actually asking and what error is a result of lazy code writing. For example, the fact that you have `private` inheritance will prevent any kind of conversion which is required to achieve what you want. If that is not the actual cause of the problem, then it is hiding the real cause. – François Andrieux Nov 10 '20 at 15:55
  • @NathanOliver, yeah, this is the right answer. Thank you! – Yehor Bublyk Nov 10 '20 at 15:58
  • @FrançoisAndrieux, okay, you're right, thanks. – Yehor Bublyk Nov 10 '20 at 16:00

3 Answers3

1

Your problem is that your lambda returns object value of type ShapeEditor which inherits from ShapeEditor and base class value is expected.

Basically you are expecting some kind of polymorphism, but you return value of some specific child type when you expect value for base type! This leads to silent conversion and as a result behavior is different then you expect so your answer is incorrect.

If you need polymorphism you need use a pointer or reference:

#include <memory>
#include <string>

class ShapeEditor {
public:
    virtual ~ShapeEditor() {}
    virtual std::string doSomething() = 0;
};

class PointEditor: public ShapeEditor {
public:
    std::string doSomething() override;
};

class RectEditor: public ShapeEditor{
public:
    std::string doSomething() override;
};

using ShapeEditorPtr = std::unique_ptr<ShapeEditor>;

class Manager {
public:
  ShapeEditorPtr (*factory)();

  Manager(ShapeEditorPtr (*_factory)());
};

void test() {
    Manager m = Manager([]() -> ShapeEditorPtr { 
        return std::make_unique<PointEditor>();
    });
}

https://godbolt.org/z/xP4czj

Marek R
  • 32,568
  • 6
  • 55
  • 140
0

@NathanOliver gave the correct answer in comments above:

Yes, but you didn't specify a return type for the lambda, so it gets it from the returned object, and that object doesn't have the same type as the return type your function pointer needs

So here is my solution:

Manager m = Manager([] -> ShapeEditor* { return new PointEditor(); });
0

as I can see that your problem is to try casting a PointEditor instance into a ShapeEditor instance. You need to cast Pointers, instead of values. So, in your lambda expression, you are returning a COPY of local instance PointEditor and trying to copy it into a ShapeEditor local variable.

Here is my proposal:

#include <cmath>
#include <cstdio>
#include <map>
#include <iostream>

using namespace std;

class ShapeEditor {
public:
    virtual void printName(){};
};

class PointEditor: ShapeEditor{
    
public:
    void printName(){cout << "PointEditor\n";};
};

class RectEditor: ShapeEditor{
    
public:
    void printName(){cout << "RectEditor\n";};
};

class Manager {
    ShapeEditor* shp;
    ShapeEditor* (*factory) ();
public:
    Manager( ShapeEditor* (*_factory)() ):factory{_factory}{
        shp = factory();
        shp->printName();
    };
    
    ~Manager(){delete shp;}
};

int main()
{
    
    ShapeEditor* (*func1)() = [](){
        PointEditor* x = new PointEditor();
        ShapeEditor* s = (ShapeEditor*)x;
        return s;
        
    };
    
    ShapeEditor* (*func2)() = [](){
        RectEditor* x = new RectEditor();
        ShapeEditor* s = (ShapeEditor*)x;
        return s;
    };
    
    Manager m1 = Manager(func1);
    Manager m2 = Manager(func2);
    
    return 0;
}

the output:

PointEditor
RectEditor
Jorge Omar Medra
  • 978
  • 1
  • 9
  • 19