3

我知道 C++ 只允许右值或临时对象绑定到常量引用。(或类似的东西......)

例如,假设我有功能doStuff(SomeValue & input)
SomeValue getNiceValue()定义:

/* These do not work */
app->doStuff(SomeValue("value1"));
app->doStuff(getNiceValue());

/* These all work, but seem awkward enough that they must be wrong. :) */

app->doStuff(*(new SomeValue("value2")));

SomeValue tmp = SomeValue("value3");
app->doStuff(tmp);

SomeValue tmp2 = getNiceValue();
app->doStuff(tmp2);

所以,三个问题:

  1. 由于我不能随意更改doStuff()or的签名getNiceValue(),这是否意味着我必须始终使用某种“名称”(即使是多余的)来传递我想要传递给的任何东西doStuff

  2. 假设,如果我可以更改函数签名,这种事情是否有共同的模式?

  3. 新的 C++11 标准会改变一切吗?C ++ 11有更好的方法吗?

谢谢

4

5 回答 5

6

在这种情况下,一个明显的问题是为什么您将doStuff其参数声明为非常量引用。如果它真的试图修改被引用的对象,那么将函数签名更改为 const 引用不是一种选择(至少不是它本身)。

无论如何,“右值”是生成临时的表达式的属性,而不是临时对象本身的属性。临时对象本身很容易成为左值,但您将其视为右值,因为产生它的表达式是右值表达式。

您可以通过在您的类中引入“右值到左值转换器”方法来解决它。比如,例如

class SomeValue {
public:
  SomeValue &get_lvalue() { return *this; }
  ...
};

现在您可以将非常量引用绑定到临时对象

app->doStuff(SomeValue("value1").get_lvalue());
app->doStuff(getNiceValue().get_lvalue());

诚然,它看起来不是很优雅,但它可能被视为一件好事,因为它可以防止你无意中做类似的事情。当然,您有责任记住临时的生命周期会延伸到完整表达式的末尾,并且不会再延长。

或者,一个类可以重载一元运算&符(具有自然语义)

class SomeValue {
public:
  SomeValue *operator &() { return this; }
  ...
};

然后可以用于相同的目的

app->doStuff(*&SomeValue("value1"));
app->doStuff(*&getNiceValue());

尽管仅出于此解决方法的目的而覆盖&运算符并不是一个好主意。它还将允许创建指向临时对象的指针。

于 2012-08-14T00:06:07.960 回答
4
  1. 由于我不能随意更改doStuff()or的签名getNiceValue(),这是否意味着我必须始终使用某种“名称”(即使是多余的)来传递我想要传递给的任何东西doStuff

几乎是的。此签名假定您要input用作“out”参数。所以作者doStuff认为,如果客户端传入一个匿名对象,那是一个逻辑错误,最好在编译时捕获。

  1. 假设,如果我可以更改函数签名,这种事情是否有共同的模式?

仅在 C++11 中,您可以像这样更改或重载:

doStuff(SomeValue&& input);

现在input只会绑定到一个右值。如果您已重载,则原始重载将获得左值,而新重载将获得右值。

  1. 新的 C++11 标准会改变一切吗?C ++ 11有更好的方法吗?

是的,请参阅上面的右值引用重载。

于 2012-08-14T00:23:57.053 回答
4

std::forward通常是“转换”价值类别的方式。但是,在作为左值转发时禁止接受右值,原因与对 non- 的引用const不会绑定到右值的原因相同。话虽如此,假设您不想超载doStuff(否则请参阅 Hinnant 的答案),您可以自己编写一个实用程序:

template<typename T>
T& unsafe_lvalue(T&& ref)
{ return ref; }

并像这样使用它:app->doStuff(unsafe_lvalue(getNiceValue())). 无需侵入性修改。

于 2012-08-14T02:25:31.893 回答
1

您必须始终为传递给 doStuff 的值使用名称。造成这种情况的原因在How come a non-const reference cannot bind to a temporary object中有详细介绍?. 简短的总结是,传递引用意味着 doStuff 可以更改它引用的值,并且将引用的值更改为临时值是编译器不应该让你做的事情。

我会避免第一个解决方案,因为它在永远不会释放的堆上分配内存。

解决此问题的常见模式是更改 doStuff 的签名以获取 const 引用。

于 2012-08-14T00:08:41.847 回答
0

不幸的是,我认为答案是你必须有一个命名对象才能传递给doStuff. 我不认为有 C++11 功能可以让您在这方面具有灵活性,我也没有听说过针对这种情况的任何设计模式。

如果这是您希望在您的程序中经常遇到的事情,我会编写一个更适合当前应用程序需求的界面。如果它只是一个关闭,我倾向于只创建一个临时对象来存储结果(就像你所做的那样)。

于 2012-08-14T00:01:29.587 回答