我有一个C ++数据结构,这是其他计算所需的“暂存器”。它并不长久,并且不经常使用,因此不具备性能要求。但是,它包括随机数生成器以及其他可更新的跟踪字段,虽然生成器的实际值并不重要,但 重要的是更新值而不是复制和重用。 这意味着通常,此类的对象通过引用传递。
如果一个实例只需要一次,最自然的方法是在需要时构建它们(可能使用工厂方法或构造函数),然后将暂存器传递给使用方法。消费者的方法签名使用传递引用,因为他们不知道这是唯一的用途,但工厂方法和构造函数按值返回 - 并且您不能通过引用传递未命名的临时工具。
有没有办法避免使用令人讨厌的临时变量来阻塞代码?我想避免以下情况:
scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);
我可以在本质上制作暂存器mutable
并且只标记所有参数const &
,但这并没有让我成为最佳实践,因为它具有误导性,我不能为类I做这个不完全控制。通过右值引用传递将需要向所有使用暂存器的消费者添加重载,这会破坏目的 - 具有清晰简洁的代码。
鉴于性能不是很关键(但是代码大小和可读性都是这样),传递这样一个暂存器的最佳实践方法是什么?如果<使用C ++ 0x功能就可以了em> required 但最好是仅限C ++ 03的功能就足够了。
编辑: 要明确,使用临时工具是可行的,这只是我想避免的代码中的不幸混乱。如果你永远不给临时名称,它显然只使用一次,并且读取的代码行越少越好。此外,在构造函数的初始化器中,不可能声明临时值。
答案 0 :(得分:4)
问题就在于此:
scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
难看?如果是这样,那么为什么不把它改成这个?:
auto useless_temp = factory(rng_parm);
答案 1 :(得分:4)
虽然将rvalues传递给接受非const引用的函数是不可行的,但是可以在rvalues上调用成员函数,但成员函数不知道它是如何被调用的。如果返回对当前对象的引用,则可以将rvalues转换为lvalues:
class scratchpad_t
{
// ...
public:
scratchpad_t& self()
{
return *this;
}
};
void foo(scratchpad_t& r)
{
}
int main()
{
foo(scratchpad_t().self());
}
请注意,即使self()
是右值,对scratchpad_t
的调用也会产生左值表达式。
如果我错了,请纠正我,但Rvalue参考参数不接受左值引用,因此使用它们需要向所有使用暂存器的消费者添加重载,这也是不幸的。
好吧,你可以使用模板......
template <typename Scratch> void foo(Scratch&& scratchpad)
{
// ...
}
如果您使用右值参数拨打foo
,Scratch
将被推断为scratchpad_t
,因此Scratch&&
将为scratchpad_t&&
。
如果您使用左值参数调用foo
,Scratch
将推断为scratchpad_t&
,并且由于参考折叠规则,Scratch&&
也将scratchpad_t&
}}
请注意,形式参数scratchpad
是名称,因此是左值,无论其类型是左值引用还是右值引用。如果要将scratchpad
传递给其他函数,则不再需要这些函数的模板技巧,只需使用左值引用参数。
顺便说一句,您确实意识到xyz.initialize_computation(scratchpad_t(1, 2, 3));
完成后,initialize_computation
中涉及的临时暂存器将被销毁,对吧?将引用存储在xyz
对象中以供以后用户使用将是一个非常糟糕的主意。
self()
不需要是成员方法,它可以是模板化函数
是的,这也是可能的,虽然我会重命名它以使意图更清楚:
template <typename T>
T& as_lvalue(T&& x)
{
return x;
}
答案 2 :(得分:3)
就个人而言,我希望看到const_cast
而不是mutable
。当我看到mutable
时,我假设某人正在做逻辑const
- 并且没有多想。 const_cast
会引发红旗,就像这样的代码。
一种选择是使用像shared_ptr
这样的东西(auto_ptr
也会起作用,具体取决于factory
正在做什么)并按值传递,这样可以避免复制成本并仅维护单个实例,但可以从您的工厂方法传入。
答案 3 :(得分:0)
我将FredOverflow的响应标记为他建议使用方法简单地返回非const引用的答案;这适用于C ++ 03。该解决方案需要一个类似于scratchpad类型的成员方法,但在C ++ 0x中我们也可以更普遍地为任何类型编写该方法:
template <typename T> T & temp(T && temporary_value) {return temporary_value;}
此函数仅转发正常的左值引用,并将右值引用转换为左值引用。当然,这样做会返回一个可修改的值,其结果将被忽略 - 这恰好正是我想要的,但在某些情况下可能看起来很奇怪。
答案 4 :(得分:0)
如果在堆中分配对象,则可以将代码转换为:
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
替换。