对不起,我之前的回答,伙计们,我在想什么?我应该正确阅读这个问题。
因此,当然,foo()
必须返回您的ResetGuard
对象以延长其生命周期,这是一件好事,而不是一件坏事。
首先,它对调用者几乎没有负担。毕竟,他/她要做的就是:
auto rg = foo ();
作为我的潜在呼叫者,foo()
我绝对不会有任何问题,@melpomene 在上面的评论中的出色建议 ( [[nodiscard]]
) 可用于确保呼叫者不会忘记这样做。
为什么强迫呼叫者这样做是一件好事(除了你在这件事上别无选择)?好吧,它让调用者有机会管理 scopeguard 的生命周期,这可能很有用(很快就会提供现场演示)。
至于这里的其他答案,我绝对不会将所有这些隐藏在宏中,因为这会隐藏潜在调用者的重要信息foo()
。相反,我会用它[[nodiscard]]
来提醒他们他们的责任,然后把它留在那里。
[编辑]
我现在在 Wandbox 上花了一点时间来完善代码,以添加全套推荐的构造函数/赋值运算符并演示 的用法[[nodiscard]]
,这对我来说是当天的发现。
首先,修改类,以(我相信)知道的人推荐的方式完成。我可以特别看到定义正确的移动构造函数的重要性(想想如果不这样做可能会遇到的细微错误)。从 JVApen 捏了一些东西 ( = delete
),对我来说看起来很明智,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 和保证复制省略,这一切都很好而且高效,所以也不需要担心。
现场演示。