使用rvalue引用作为默认参数

时间:2014-08-11 21:55:33

标签: c++ pass-by-reference move-semantics rvalue-reference default-arguments

我想创建一个函数,它接受一个对象的可选引用,如果没有提供,则在函数的持续时间内创建一个函数,即

void Foo(Bar& b = Bar()) { /* stuff */ }

这当然是无效代码,因为Bar无法在此上下文中隐式转换为Bar引用。引用不能是const,因为b在函数内部发生了变异。

你可以通过使用右值引用来解决这个问题,即

void Foo(Bar&& b = Bar()) { /* stuff */ }

这是否有效使用右值参考?调用者现在必须在他们的std::move参数上调用Bar,即使我无意清除传递的Bar,通常情况下你传递rvalues。

2 个答案:

答案 0 :(得分:10)

void Foo(Bar&& b = Bar()) { /* stuff */ }

这肯定是对r值引用的有效使用,但它并没有以任何方式反映实际的语义,因此“被设计破坏”。

您要做的是使用转发器函数提供默认参数,如下所示:

void Foo(Bar& b) { /* stuff */ }
void Foo() { Bar b{}; Foo(b); }

或者使用静态default-argument(请注意,它总是重用相同的对象):

template<class T> decltype(T{})& default_object() {static T x{}; return x;}
void Foo(Bar& b = default_object<Bar>()) { /* stuff */ }

like KerrekSB proposes in a comment(我添加constexpr)使用此危险模板功能:

template<class T> constexpr T& no_move(T&& t) { return t; }
void Foo(Bar& b = no_move(Bar{})) { /* stuff */ }

答案 1 :(得分:7)

所以你有一个函数,它接受一个要修改的输入输出参数,以便将信息传递给调用者。

但是你希望参数是可选的。

因此,您的解决方案是通过要求调用者移动参数(这通常意味着他们丢失了他们拥有的状态或可能处于未指定状态)来使参数看起来像是in参数。这是一个糟糕的,糟糕的设计。您将使用为了方便函数内部实现而创建的奇怪API混淆调用者。您应该为用户而不是实现者设计API。

您可以执行Deduplicator建议并将其拆分为两个函数,一个提供虚拟对象作为输入输出参数然后丢弃:

void Foo(Bar& b) { /* stuff */ }
void Foo() { Bar dummy{}; Foo(dummy); }

或者你想要的是一个可以为null的引用,停止使用引用并使用正确的语言功能通过引用传递一些东西,而不是:

void Foo(Bar* b) { /* stuff, updating b if not null */ }