我正在尝试设计一个像这样的简单函数:
void draw(Callback callback)
{
Drawing drawing("name");
callback(drawing);
std::cout << drawing.name() << std::endl;
}
wheredrawing
应该作为引用传递给回调,以便调用者可以像这样修改它:
draw([](auto& drawing) {
drawing.set_name("another name");
});
现在这可以工作了,因为我将回调参数显式键入为auto&
(或Drawing&
)。但是,当我仅将参数键入为auto
( 或Drawing
) 时,代码也会编译,但不再按预期工作。该drawing
实例是复制的,而不是通过引用传递的。
当 lambda 参数未显式键入为引用时,我希望代码不再编译。
我尝试过以下方法:
using Callback = std::function<void(Drawing&)>;
:drawing
当未明确键入 as 时不会被修改auto&
。template<typename T> concept Callback = std::is_invocable<void, T, Drawing&>;
: 和上面一样。using Callback = void (*)(Drawing&);
:这确实有效,但我失去了使用捕获的能力。
那么我该如何正确输入 Callback 以便只有Drawing&
lambda 才是有效的参数呢?
您可以进行限制,
draw
以便不能使用右值接受来调用它callback
:如果给定的
callback
接受auto
或auto&&
,它将不满足约束:请参阅编译器资源管理器中的实时示例。
为什么这有效
唯一不能绑定到右值的引用是非常量左值引用。通过限制函数以使回调不能接受右值,我们取消了采用
Drawing
、Drawing&&
、const Drawing&
和 的回调const Drawing&&
,因为所有这些都可以绑定到右值。本质上,我们强制用户采取
Drawing&
(或auto&
),并且意外复制是不可能的,因为左值引用无法绑定到临时对象。注意:在进行任何分析之前,右值引用都会转换为相同类型的 x 值;这就是为什么我说“右值”而不是谈论这些项目符号中的引用。
进一步建议
请注意,如果您的目标是
Drawing
不意外复制,则可以通过显式删除其复制构造函数和复制赋值运算符来更可靠地防止这种情况。然而,这显然适用于任何地方,而不仅仅是在draw
.