#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
элемента может быть перемещено.