Reputation: 1062
I am trying to create a a chained builder in C++ as follows:
#include <string>
#include <vector>
using std::string;
using std::vector;
class AbstractBuilder {
protected:
AbstractBuilder&
add_field(string name, string value) {
// blabla, add field to json structure
return *this;
}
};
class Event {
public:
class Builder : public AbstractBuilder {
public:
Builder& event(string event) {
return add_field("event", event);
}
Builder& payload(string payload) {
return add_field("payload", payload);
}
};
};
Event::Builder builder = Event::Builder().event("test");
The error I am getting is:
gcc test.cpp
test.cpp:22:14: error: non-const lvalue reference to type 'Event::Builder' cannot bind to a value of unrelated type 'AbstractBuilder'
return add_field("event", event);
^~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:25:14: error: non-const lvalue reference to type 'Event::Builder' cannot bind to a value of unrelated type 'AbstractBuilder'
return add_field("payload", payload);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.
I am a C++ noob, and don't quite understand the issue and how to resolve it. AFAIU the object returned from the add_field in the Event::Builder is going to be a reference to self, of type Event::Builder, not an abstract temp/rvalue. What am I missing? What might be a better way to achieve this (build a set of strong typed builders for some json structures). Any existing examples are welcome.
Thanks
Upvotes: 0
Views: 1544
Reputation: 16963
AFAIU the object returned from the add_field in the Event::Builder is going to be [...]
You are applying higher level reasoning. What is the compiler allowed to assume? It sees that add_field()
returns AbstractBuilder&
and you are trying to return that (from event()
and payload()
) as a Builder&
. How can this be done? In general, it cannot since not every AbstractBuilder
is a Builder
.
If you are absolutely certain that add_field
will return *this
(even after future modifications to the code), then you can explicitly tell the compiler to assume that the returned reference is a sub-object of a Builder
.
return static_cast<Builder&>(add_field("event", event));
This is fragile though, since the compiler cannot detect if your assumption breaks (in the future). You might want to add a runtime assertion to detect breakage. Less fragile would be to force these functions to return *this
:
add_field("event", event);
return *this;
This has its own drawback in that your code repeats itself. (If you ever change the semantics of what these functions return, you'll have multiple places to change.) There is a judgment call to be made as to which approach is preferable, or perhaps if the overall design should be rethought.
Upvotes: 2