Consider the following code:
int x = 3;
auto f1 = [x]() mutable
{
return x++;
};
auto f2 = [f1]()
{
return f1();
};
Run Code Online (Sandbox Code Playgroud)
This will not compile, because f1()
is not const, and f2
is not declared as mutable. Does this mean that if I have a library function that accepts an arbitrary function argument and captures it in a lambda, I always need to make that lambda mutable, because I don't know what users can pass in? Notably, wrapping f1
in std::function
seems to resolve this problem (how?).
lub*_*bgr 21
Does this mean that if I have a library function that accepts an arbitrary function argument and captures it in a lambda, I always need to make that lambda mutable, because I don't know what users can pass in?
That's a design decision for your library API. You can require client code to pass function objects with a const
-qualified operator()
(which is the case for non-mutable
lambda expressions). If something different is passed, a compiler error is triggered. But if the context might require a function object argument that modifies its state, then yes, you have to make the internal lambda mutable
.
另一种选择是分派调用给定函数类型operator()
的const
-qualified实例的能力。遵循这些原则(请注意,这需要同时修复带有const
和和的函数对象const
operator()
,这会导致歧义):
template <class Fct>
auto wrap(Fct&& f) -> decltype(f(), void())
{
[fct = std::forward<Fct>(f)]() mutable { fct(); }();
}
template <class Fct>
auto wrap(Fct&& f) -> decltype(std::declval<const Fct&>()(), void())
{
[fct = std::forward<Fct>(f)]() { fct(); }();
}
Run Code Online (Sandbox Code Playgroud)
值得注意的是,将f1包装在std :: function中似乎可以解决此问题(如何?)。
std::function
由于其类型擦除和复制语义,这是一个错误。它允许调用非const
限定符operator()
,可以使用以下代码段进行验证:
const std::function<void()> f = [i = 0]() mutable { ++i; };
f(); // Shouldn't be possible, but unfortunately, it is
Run Code Online (Sandbox Code Playgroud)
这是一个已知问题,值得检查蒂特斯·温特(Titus Winter)对此的抱怨。
首先,我要解决您的第二个问题。std::function
类型将擦除,并保留使用其初始化的函子的副本。这意味着std::function::operator()
与实际函子之间存在一个间接层operator()
。
设想是否愿意,通过指针将某些内容保存在班级中。然后,您可以从类的const成员函数中对pointee调用变异操作,因为它不会(在浅表中)影响类所持有的指针。这与您观察到的情况类似。
至于您的第一个问题,“总是”一词太强了。这取决于您的目标。
如果要轻松支持自变异函子,则应捕获可变的lambda。但是请注意,这可能会影响您现在可以调用的库函数。
如果您希望支持非变异运算,请使用非变异lambda。我之所以说“偏爱”,是因为正如我们观察到的那样,可以通过额外的间接级别来“欺骗”类型系统。因此,您偏爱的方法只会变得更易于使用,并非不可能。就像明智的建议一样,使正确使用API变得容易,而错误使用起来也会更加困难。