使用rvalue引用作为默认参数

Mat*_*ine 6 c++ pass-by-reference rvalue-reference move-semantics default-arguments

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

void Foo(Bar& b = Bar()) { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)

当然,这是无效代码,因为在此上下文中Bar无法隐式转换为Bar引用.引用不能const,就像b函数内部的变异一样.

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

void Foo(Bar&& b = Bar()) { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)

这是rvalue引用的有效用法吗?调用者现在必须调用std::move他们的Bar参数,即使我无意清除传递Bar,通常是在传递rvalues时的情况.

Ded*_*tor 10

void Foo(Bar&& b = Bar()) { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)

这当然是对r值引用的有效使用,但它不以任何方式反映实际的语义,因此"被设计破坏".

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

void Foo(Bar& b) { /* stuff */ }
void Foo() { Bar b{}; Foo(b); }
Run Code Online (Sandbox Code Playgroud)

或者使用静态default-argument(注意这总是重用同一个对象):

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

或者像KerrekSB在评论中提出的(我添加constexpr)使用这个危险的模板函数:

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


Jon*_*ely 7

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

但是您希望参数是可选的.

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

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

void Foo(Bar& b) { /* stuff */ }
void Foo() { Bar dummy{}; Foo(dummy); }
Run Code Online (Sandbox Code Playgroud)

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

void Foo(Bar* b) { /* stuff, updating b if not null */ }
Run Code Online (Sandbox Code Playgroud)