3

我有一个 C++ 数据结构,它是其他计算所需的“便签本”。它的寿命不长,并且不经常使用,因此对性能不是很重要。但是,它包括一个随机数生成器以及其他可更新的跟踪字段,虽然生成器的实际值并不重要,但重要的是更新值而不是复制和重用。 这意味着一般来说,此类的对象是通过引用传递的。

如果一个实例只需要一次,最自然的方法是在需要的地方构造它们(可能使用工厂方法或构造函数),然后将暂存器传递给使用方法。消费者的方法签名使用按引用传递,因为他们不知道这是唯一的用途,但是工厂方法和构造函数按值返回 - 您不能通过引用传递未命名的临时对象。

有没有办法避免用讨厌的临时变量阻塞代码?我想避免以下事情:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);

我可以在本质上制作暂存器mutable并标记所有参数const &,但这并没有让我觉得这是最佳实践,因为它具有误导性,而且对于我无法完全控制的课程,我不能这样做。通过右值引用传递将需要向所有暂存器消费者添加重载,这违背了目的 - 具有清晰简洁的代码。

考虑到性能并不重要(但代码大小和可读性是关键),传递这样一个暂存器的最佳实践方法是什么?如果需要, 可以使用 C++0x 功能,但最好仅使用 C++03 功能就足够了。

编辑: 需要明确的是,使用临时是可行的,这只是我想避免的代码中的不幸混乱。如果你从不给​​临时文件起个名字,它显然只使用过一次,而且要阅读的代码行数越少越好。此外,在构造函数的初始化程序中,不可能声明临时对象。

4

5 回答 5

4

问题只是这个:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);

丑吗?如果是这样,那为什么不改成这样呢?:

auto useless_temp = factory(rng_parm);
于 2010-09-16T21:58:48.217 回答
4

虽然不能将右值传递给接受非常量引用的函数,但可以在右值上调用成员函数,但成员函数不知道它是如何被调用的。如果返回对当前对象的引用,则可以将右值转换为左值:

class scratchpad_t
{
    // ...

public:

    scratchpad_t& self()
    {
        return *this;
    }
};

void foo(scratchpad_t& r)
{
}

int main()
{
    foo(scratchpad_t().self());
}

请注意调用如何self()产生一个左值表达式,即使它scratchpad_t是一个右值。

如果我错了,请纠正我,但右值引用参数不接受左值引用,因此使用它们需要向所有暂存器消费者添加重载,这也是不幸的。

好吧,你可以使用模板...

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;
}
于 2010-09-16T22:30:12.113 回答
3

就个人而言,我宁愿看到而const_cast不是mutable。当我看到mutable时,我假设某人正在做逻辑性const,并且不要想太多。const_cast但是会引发危险信号,因为这样的代码应该如此。

一种选择是使用类似shared_ptr(auto_ptr也可以根据factory正在执行的操作) 并按值传递它,这样可以避免复制成本并仅维护一个实例,但可以从您的工厂方法传入。

于 2010-09-16T22:15:12.623 回答
0

我将 FredOverflow 的回复标记为他建议使用方法来简单地返回非常量引用的答案;这适用于 C++03。该解决方案需要每个类暂存器类型的成员方法,但在 C++0x 中,我们也可以更一般地为任何类型编写该方法:

template <typename T> T & temp(T && temporary_value) {return temporary_value;}

该函数只是转发普通的左值引用,并将右值引用转换为左值引用。当然,这样做会返回一个可修改的值,其结果会被忽略——这恰好是我想要的,但在某些情况下可能看起来很奇怪。

于 2010-09-17T07:43:34.850 回答
0

如果您在堆中分配对象,您可能可以将代码转换为:

std::auto_ptr<scratch_t> create_scratch();

foo( *create_scratch() );

工厂auto_ptr在堆栈中创建并返回一个对象,而不是一个对象。返回的auto_ptr临时对象将拥有该对象的所有权,但您可以在临时对象上调用非常量方法,并且您可以取消引用指针以获取真正的引用。在下一个序列点,智能指针将被销毁并释放内存。如果您需要将相同scratch_t的内容连续传递给不同的函数,您可以捕获智能指针:

std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );

std::unique_ptr这可以在即将发布的标准中替换。

于 2010-09-17T07:50:54.397 回答