Как получить доступ к возвращаемому значению цепной переменной

#c

Вопрос:

Я пытаюсь создать класс, который поддерживает следующий API:

 auto amp;amp;container = TestContainer::create().add("Hello").add("World");
 

Итак, я создал класс, в котором add функции возвращают ссылку на экземпляр:

 class TestContainer {
  public:
    std::vector<String> values;
        
    TestContainer amp;add(const String amp;string) {
      values.push_back(string);
      return *this;
    }
        
        
    static TestContainer create() {
      return TestContainer();
    }
};    
 

Однако по какой-то причине, когда я обращаюсь container к переменной, вектор внутри нее пуст. Итак, я попробовал другой подход:

 auto amp;amp;container = TestContainer::create();

container.add("Hello").add("World");
 

Это работает, потому что я ссылаюсь на исходный контейнер; однако, когда я пытаюсь сослаться на «последний» контейнер в цепочке, ссылка теряется и вызывается деструктор.

Что я могу сделать здесь, чтобы получить API, от которого мне не нужно отделяться create add ?

Комментарии:

1. auto amp;amp;container = ... -> auto container = ... ?

2. Уже ответил, но вы создаете ссылку на временную, которая немедленно уничтожается. (Ссылка на временное иногда продлевает срок службы временного, но это тот случай, когда это невозможно.)

Ответ №1:

Почему вы используете amp;amp; в объявлении container переменную?

Попробуй

 auto container=TestContainer::create().add()...;
 

или еще лучше:

 auto container=TestContainer().add(...).add(...);
 

Нет необходимости в функции создания.

Дополнительная информация: TestContainer() создает новый (временный) объект типа TestContainer . .add(...) вызывает add функцию для этого нового (временного) объекта и передает значение члену. Он возвращает ссылку на себя, так что вы можете связывать вызовы по цепочке add .

Затем, наконец, когда вы закончите вызов add, временный объект будет назначен объекту container . Или, скорее, — поскольку компилятору разрешено оптимизировать = away, новый объект создается из временного объекта.

 TestContainer container(TestContainer().add().add().add());
 

это сработало бы так же хорошо.

Комментарии:

1. Не могли бы вы объяснить, пожалуйста, почему использование ссылки или ссылки на значение r является неправильным? Я не хочу, чтобы созданный объект копировался.

2. @Gasim Вы создаете ссылку на что ?

3. Механизм называется «Copy elision». en.cppreference.com/w/cpp/language/copy_elision Когда вы «назначаете» вновь созданный объект с помощью вызова конструктора, компилятору разрешается пропустить назначение и вместо этого инициализировать новый объект. То же самое происходит и с возвращаемыми значениями

4. Этот ответ не объясняет, почему autoamp;amp; container = TestContainer::create(); это работает. container здесь тоже есть ссылка.

Ответ №2:

Цепочка функций — членов прерывает продление срока службы после окончания цепочки.

То, что вы могли бы сделать, — это что-то вроде этого:

 class TestContainer {
  public:
    std::vector<std::string> values;
    
    TestContainer amp; add(const std::string amp;string) amp; {
        values.push_back(string);
        return *this;
    }
    
    TestContainer add(const std::string amp;string) amp;amp; {
      values.push_back(string);
      return std::move(*this);
    }
        
    static TestContainer create() {
      return TestContainer();
    }
};    

int main()
{
    auto amp;amp;container = TestContainer::create().add("Hello").add("World");
   
}
 

Или это:

 class TestContainer {
  public:
    std::vector<std::string> values;
  
    TestContainer amp; add(const std::string amp;string) {
        values.push_back(string);
        return *this;
    }

    TestContainer end_chain() amp; { return std::move(*this); } 
        
    static TestContainer create() {
      return TestContainer();
    }
};    

int main()
{
    auto amp;amp;container = TestContainer::create().add("Hello").add("World").end_chain();
   
}
 

У обоих решений есть свои недостатки. Для первого у вас есть дублирование кода, для второго вам нужно знать, что вы должны позвонить end_chain .

И это создаст дополнительный объект. Но содержимое std::vector элемента может быть перемещено.