В процессе одного из споров на RSDN-е родился пример того, как компилятор, применив NRVO, приводит к неожиданному поведению.
Под катом полный код примера. Пока же пару слов о том, на что смотреть.
Есть простой класс entity, который содержит внутри себя целочисленное значение. Изменить это целочисленное значение можно только вызывав метод entity::update. Но это не const-метод, т.е. для константного entity его вызывать нельзя.
Фокус с entity в том, что есть еще и entity_registry, т.е. реестр объектов entity. Если в конструктор entity передать ссылку на entity_registry, то entity себя в реестре зарегистрирует. А в деструкторе -- вычеркнет.
Пока объект находится в реестре, его можно изменить через реестр: вызываем метод entity_registry::update и реестр вызывает update для всех зарегистрированных в нем объектов.
Но важно отметить, что если объект entity создавался без передачи ему в конструктор ссылки на реестр, то он ни в каких реестрах не регистрируется, поэтому через реестр его изменить нельзя. Так что в такой ситуации:
void check() { entity_registry registry; entity e1{registry}; entity e2; // Не в реестре. entity e3{e1}; // Не в реестре. registry.update(25); std::cout << e1.value() << ", " << e2.value() << ", " << e3.value() << std::endl; } |
Мы получим вполне ожидаемое:
add to registry: 0x7ffd7b567c18
25, 0, 0
removed from registry: 0x7ffd7b567c18
Т.е. объект e1 добавил себя в реестр, потом мы получили три предсказуемых значения (два нулевых, т.к. никто не менял e2 и e3), затем e1 изъял себя из реестра.
А теперь внимание, вопрос: что будет во в таком случае?
entity make_entity(entity_registry & registry) { entity ent{registry}; ent.update(42); return ent; } void demo() { entity_registry registry; const entity ent = make_entity(registry); registry.update(100); std::cout << ent.value() << std::endl; } |
Можем ли мы изменть значение константного ent внутри функции demo через вызов update у реестра?