【发布时间】:2019-06-07 10:31:33
【问题描述】:
在我的一个项目中,我使用了一个小型实用函数,它采用 Message 结构和 lambda 函数来修改此消息结构。
现在,我无意中传递了一个没有必要引用 & 的 lambda。它可以完美编译,但没有提供所需的输出。
就我而言,应该是以下两种行为之一:
- 忘记写
auto&,而只写auto会导致编译错误 - 只写
auto应解释为auto&。
如果缺少&,是否可以阻止编译,或者更好地将auto自动解释为auto&?
#include <iostream>
#include <functional>
#include <boost/variant.hpp>
struct Message {
int x;
int y;
};
void changeMessage(Message& m, const std::function<void(Message&)>& messageModifier) {
std::cout << "Message before:" << m.x << " " << m.y << "\n";
messageModifier(m);
std::cout << "Message after:" << m.x << " " << m.y << "\n";
}
int main(int, char**) {
{
std::function<void(int&)> f = [](int&) {};
std::function<void(int)> g = [](int) {};
f = g; // This compiles.
}
{
std::function<void(int&)> f = [](int&) {};
std::function<void(int)> g = [](int) {};
//g = f; // This does not compile. Makes perfect sense.
}
Message m{ 10,20 };
{
changeMessage(m, [](auto m) { m.x++; m.y--; }); // User unintentionally forgot &! Can I prevent this from compilation?
std::cout << "Message outside: " << m.x << " " << m.y << "\n";
}
{
changeMessage(m, [](auto& m) { m.x++; m.y--; });
std::cout << "Message outside: " << m.x << " " << m.y << "\n";
}
}
【问题讨论】:
-
简短回答:不,C++ 不能以这种方式工作。防止可调用对象的参数成为非引用类型的唯一方法是,在这种情况下,可调用对象本身的某些内容无法编译。当然,有一些奇特的解决方案涉及使用基于模板的脚本生成代码或声明,这将确保参数具有正确的类型。
-
“或者甚至更好地将
auto自动解释为auto&?” - 如果可能的话,恐怕会发生多少破损。不,这无论如何都不是更好。您要求替换一个您可以通过打开可能需要数小时或数天才能追踪的错误轻松发现的错误。处理悬空引用比较困难。 -
使
Message成为不可复制的对象:Message(const Message&) = delete;。 -
从技术上讲,您可以使用
!is_invocable_v<..., Message&&>之类的东西来确保 lambda 不接受右值。 -
您可以要求 lambda 返回修改后的消息。那将使您担心的那种错误变得不可能。这是使用值语义的优势之一。
标签: c++ lambda closures std-function