解决对临时对非const引用的C ++限制

时间:2010-09-16 21:48:18

标签: c++ reference c++11 const temporaries

我有一个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的功能就足够了。

编辑: 要明确,使用临时工具是可行的,这只是我想避免的代码中的不幸混乱。如果你永远不给临时名称,它显然只使用一次,并且读取的代码行越少越好。此外,在构造函数的初始化器中,不可能声明临时值。

5 个答案:

答案 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)
{
    // ...
}

如果您使用右值参数拨打fooScratch将被推断为scratchpad_t,因此Scratch&&将为scratchpad_t&&

如果您使用左值参数调用fooScratch将推断为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替换。