Извините за мой предыдущий ответ, ребята, что было Я думаю? Я должен был правильно прочитать вопрос.
Итак, конечно, foo()
должен вернуть ваш ResetGuard
объект, чтобы продлить срок его службы, , и это хорошая вещь , а не плохая вещь.
Во-первых, это вряд ли обременительно для звонящего. В конце концов, все, что он / она должен сделать, это:
auto rg = foo ();
В качестве потенциального абонента foo()
у меня не было бы абсолютно никаких проблем с этим, и предложение @ mel5men превосходное в комментариях выше ([[nodiscard]]
) может быть использовано для того, чтобы вызывающие абоненты не забудьте сделать это.
И почему принуждение вызывающего абонента делать это хорошо (не считая того, что у вас все равно нет выбора)? Что ж, это дает абоненту возможность управлять временем жизни охраны области видимости, и это может быть полезно (скоро появится живая демоверсия).
Что касается других ответов здесь, я бы определенно не скрывал все это в макросе, потому что он скрывает важную часть информации от потенциальных абонентов foo()
. Вместо этого я бы использовал [[nodiscard]]
, чтобы напомнить им об их обязанностях и оставить все как есть.
[Изменить]
Я потратил немного времени на Wandbox, дорабатывая код, чтобы добавить полный набор рекомендуемых конструкторов / операторов присваивания и продемонстрировать использование [[nodiscard]]
, что для меня является находкой дня.
Во-первых, модифицированный класс, сделанный таким образом (я полагаю), который рекомендуют те, кто знает. Я особенно вижу важность определения правильного конструктора перемещения (просто подумайте о тонких ошибках, с которыми вы можете столкнуться, если не сделаете этого). Ущипнул кое-что (= delete
) от JVApen, мне кажется мудрым, TU JV.
#include <iostream>
#include <assert.h>
#define INCLUDE_COPY_MOVE_SWAP_STUFF
template <class T> class [[nodiscard]] ResetGuard
{
public:
ResetGuard (T& obj_to_reset, const T& new_value) : old_value (obj_to_reset), obj_to_reset (obj_to_reset)
{
obj_to_reset = new_value;
}
#ifdef INCLUDE_COPY_MOVE_SWAP_STUFF
ResetGuard (const ResetGuard& copy_from) = delete;
ResetGuard &operator= (const ResetGuard& copy_assign_from) = delete;
ResetGuard &operator= (ResetGuard&& move_assign_from) = delete;
ResetGuard (ResetGuard&& move_from) : old_value (move_from.old_value), obj_to_reset (move_from.obj_to_reset)
{
assert (!move_from.defunct);
move_from.defunct = true;
}
#endif
~ResetGuard()
{
if (!defunct)
obj_to_reset = old_value;
}
private:
T old_value;
T& obj_to_reset;
bool defunct = false;
};
Прокомментируйте #define INCLUDE_COPY_MOVE_SWAP_STUFF
, чтобы увидеть предупреждение компилятора, которое вы получите, если не будете делать все то, что должны.
Тестовая программа:
int GLOBAL_VALUE = 0;
ResetGuard<int> temporarily_set_global_value (int new_val)
{
return { GLOBAL_VALUE, new_val }; // updates GLOBAL_VALUE
}
void bad_foo()
{
temporarily_set_global_value (15);
std::cout << "GLOBAL_VALUE in bad_foo () is " << GLOBAL_VALUE << std::endl;
}
void good_foo()
{
auto rg = temporarily_set_global_value (15);
std::cout << "GLOBAL_VALUE in good_foo () is " << GLOBAL_VALUE << std::endl;
}
auto better_foo()
{
auto rg = temporarily_set_global_value (15);
std::cout << "GLOBAL_VALUE in better_foo () is " << GLOBAL_VALUE << std::endl;
return rg;
}
int main ()
{
bad_foo ();
good_foo ();
std::cout << "GLOBAL_VALUE after good_foo () returns is " << GLOBAL_VALUE << std::endl;
{
auto rg = better_foo ();
std::cout << "GLOBAL_VALUE after better_foo () returns is " << GLOBAL_VALUE << std::endl;
{
auto rg_moved = std::move (rg);
std::cout << "GLOBAL_VALUE after ResetGuard moved is " << GLOBAL_VALUE << std::endl;
}
std::cout << "GLOBAL_VALUE after ResetGuard moved to goes out of scope is " << GLOBAL_VALUE << std::endl;
GLOBAL_VALUE = 42;
}
std::cout << "GLOBAL_VALUE after ResetGuard moved from goes out of scope is " << GLOBAL_VALUE << std::endl;
}
Выход компилятора:
prog.cc: In function 'void bad_foo()':
prog.cc:47:38: warning: ignoring returned value of type 'ResetGuard<int>', declared with attribute nodiscard [-Wunused-result]
temporarily_set_global_value (15);
^
prog.cc:40:17: note: in call to 'ResetGuard<int> temporarily_set_global_value(int)', declared here
ResetGuard<int> temporarily_set_global_value (int new_val)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:6:40: note: 'ResetGuard<int>' declared here
template <class T> class [[nodiscard]] ResetGuard
^~~~~~~~~~
Вывод программы:
GLOBAL_VALUE in bad_foo () is 0
GLOBAL_VALUE in good_foo () is 15
GLOBAL_VALUE after good_foo () returns is 0
GLOBAL_VALUE in better_foo () is 15
GLOBAL_VALUE after better_foo () returns is 15
GLOBAL_VALUE after ResetGuard moved is 15
GLOBAL_VALUE after ResetGuard moved to goes out of scope is 0
GLOBAL_VALUE after ResetGuard moved from goes out of scope is 42
Так что у вас это есть. Если вы делаете все то, что должны делать (и я надеюсь, что у меня есть!), То все работает просто отлично, и все это хорошо и эффективно благодаря RVO и гарантированному разрешению копирования, так что об этом также не нужно беспокоиться.
Живая демоверсия .