围绕C ++的限制,对临时性的非const。
-
03-10-2019 - |
题
我有一个C ++数据结构,这是其他计算所需的“ scratchpad”。它不是长寿的,也不经常使用,因此性能也不关键。但是,它在其他可更新的跟踪字段中包括一个随机数生成器,尽管生成器的实际值并不重要,但它 是 重要的是要更新值而不是复制和重复使用。 这意味着通常,该类的对象是通过引用传递的。
如果仅需要一次实例,则最自然的方法是在需要的任何地方构造它们(也许是使用工厂方法或构造函数),然后将Scratchpad传递到消费方法。消费者的方法签名使用通过引用使用,因为他们不知道这是唯一的用途,但是出厂方法和构造函数按值返回 - 您不能通过参考来传递未命名的临时性。
有没有办法避免用讨厌的临时变量堵塞代码?我想避免以下内容:
scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);
我可以本质上做刮擦板 mutable
只需标记所有参数 const &
, ,但这并没有使我成为最佳实践,因为它具有误导性,而且我不能为我无法完全控制的课程做到这一点。通过RVALUE参考,将需要向所有ScratchPad的消费者添加过载,这会破坏目的 - 具有清晰简洁的代码。
鉴于性能不是关键(而是代码大小和可读性), 在这种刮擦板中传递的最佳实践方法是什么? 使用C ++ 0x功能是可以的 必需的 但最好是C ++仅03个功能就足够了。
编辑: 明确地说,使用临时性是可行的,这只是我想避免的代码中不幸的混乱。如果您从不给临时名称命名,那么它显然只使用了一次,并且读取的代码行越少,越好。同样,在构造师的初始化器中,不可能宣布临时工。
解决方案
虽然将rvalues传递给接受非const引用的函数是不行的,但是可以在rvalues上调用成员函数,但是成员函数不知道如何调用。如果您返回对当前对象的引用,则可以将RVALUES转换为LVALUE:
class scratchpad_t
{
// ...
public:
scratchpad_t& self()
{
return *this;
}
};
void foo(scratchpad_t& r)
{
}
int main()
{
foo(scratchpad_t().self());
}
注意如何呼吁 self()
即使 scratchpad_t
是一个rvalue。
如果我错了,请纠正我,但是RVALUE参考参数不接受LVALUE参考,因此使用它们需要向所有ScratchPad的消费者添加过载,这也很不幸。
好吧,您可以使用模板...
template <typename Scratch> void foo(Scratch&& scratchpad)
{
// ...
}
如果您打电话 foo
使用RVALUE参数, Scratch
将被推论 scratchpad_t
, , 因此 Scratch&&
将会 scratchpad_t&&
.
如果你打电话 foo
使用LVALUE参数, Scratch
将被推论 scratchpad_t&
, ,并且由于参考折叠规则, Scratch&&
也将是 scratchpad_t&
.
请注意,正式参数 scratchpad
是名称,因此是LVALUE,无论其类型是LVALUE参考还是RVALUE参考。如果你想通过 scratchpad
在其他功能上,您不再需要这些功能的模板技巧,只需使用lvalue参考参数即可。
顺便说一句,您确实意识到涉及的临时刮擦 xyz.initialize_computation(scratchpad_t(1, 2, 3));
将立即被销毁 initialize_computation
完成了,对吗?在内部存储参考 xyz
以后用户的对象将是一个极其糟糕的主意。
self()
不需要是会员方法,它可以是模板功能
是的,这也是可能的,尽管我会重命名以使意图更清晰:
template <typename T>
T& as_lvalue(T&& x)
{
return x;
}
其他提示
问题就是这样:
scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
丑陋吗?如果是这样,那为什么不将其更改为呢?
auto useless_temp = factory(rng_parm);
就个人而言,我宁愿看到 const_cast
比 mutable
. 。当我看见 mutable
, ,我假设有人在做逻辑 const
- 不考虑太多。 const_cast
但是,将危险信号升起,因为这样的代码应该。
一种选择是使用类似的东西 shared_ptr
(auto_ptr
会根据什么来工作 factory
正在执行)并按值传递,这避免了复制成本并仅维护一个实例,但可以从您的工厂方法传递。
如果您在堆中分配对象,则可以将代码转换为类似的内容:
std::auto_ptr<scratch_t> create_scratch();
foo( *create_scratch() );
工厂创建并返回 auto_ptr
而不是堆栈中的对象。返回 auto_ptr
临时性将占据对象的所有权,但是您可以暂时调用非const方法,您可以取消指针以获取真实参考。在下一个序列点,智能指针将被破坏并释放。如果您需要通过相同 scratch_t
对于一排不同的功能,您可以捕获智能指针:
std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );
可以替换 std::unique_ptr
在即将到来的标准中。
我将Fredoverflow的回答标记为他的建议,以使用一种方法来简单地返回非const参考。这在C ++ 03中起作用。该解决方案需要类似于SCRATCHPAD类型的成员方法,但是在C ++ 0x中,我们还可以更一般地为任何类型编写该方法:
template <typename T> T & temp(T && temporary_value) {return temporary_value;}
此函数只是转发正常的LVALUE参考,然后将RVALUE引用转换为LVALUE参考。当然,这样做会返回一个可修改的值,其结果被忽略了 - 这恰好是我想要的,但在某些情况下似乎很奇怪。