Цепной шаблон построителя в C ++ - PullRequest
0 голосов
/ 20 июня 2020

Я пытаюсь создать связанный построитель на C ++ следующим образом:

#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");

Я получаю следующую ошибку:

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.

Я новичок C ++ и не Я прекрасно понимаю, в чем проблема и как ее решить. AFAIU объект, возвращаемый из add_field в Event :: Builder, будет ссылкой на себя типа Event :: Builder, а не абстрактным значением temp / rvalue. Что мне не хватает? Что может быть лучшим способом добиться этого (создать набор построителей строгого типа для некоторых структур json). Любые существующие примеры приветствуются.

Спасибо

1 Ответ

1 голос
/ 20 июня 2020

AFAIU объект, возвращаемый из add_field в Event :: Builder, будет [...]

Вы применяете рассуждение более высокого уровня. Что может предполагать компилятор? Он видит, что add_field() возвращает AbstractBuilder&, и вы пытаетесь вернуть это (из event() и payload()) как Builder&. Как это может быть сделано? В общем, это невозможно, поскольку не каждый AbstractBuilder является Builder.

Если вы абсолютно уверены, что add_field вернет *this (даже после будущих изменений кода), вы можете явно указать компилятору предположить, что возвращенная ссылка является подобъектом Builder.

return static_cast<Builder&>(add_field("event", event));

Однако это fr agile, поскольку компилятор не может определить, нарушается ли ваше предположение (в будущее). Возможно, вы захотите добавить утверждение времени выполнения для обнаружения поломки. Меньше fr agile будет заставлять эти функции возвращать *this:

add_field("event", event);
return *this;

Это имеет свой собственный недостаток в том, что ваш код повторяется. (Если вы когда-нибудь измените семантику того, что возвращают эти функции, у вас будет несколько мест для изменения.) Необходимо сделать вывод о том, какой подход предпочтительнее, или, возможно, следует переосмыслить общий дизайн.

...