在lambda表达式中捕获指针?

6

我有一个使用lambda表达式的函数。

std::vector<Bar*> mBars;

void foo(Bar* bar)
{
    auto duplicateBars = std::remove_if(mBars.begin(), mBars.end(),
        [bar] (const Bar* const &element)
        {
            return bar == element;
        });

    mBars.erase(duplicateBars, mBars.end());
}

稍后,我审查了代码并意识到可以在foo的签名中添加两个常量。
void foo(const Bar* const bar);
bar的指针和数据现在是常量,但是对于lambda表达式来说,指针本身是常量,因为我通过值捕获。然而,指向的数据可以被更改,因为lambda捕获中不允许使用const。这对我来说有些不直观。我的理解正确吗?我可以使用第二个签名,但是无法保护lambda表达式中的数据免受更改。

你究竟想要实现什么?指针可以在lambda中使用,无论是const还是非const:http://ideone.com/GalbBA - Bruno Ferreira
3
现在问题确切是什么?如果将foo定义为foo(Bar const * const bar),那么你不能使用被 lambda 捕获的 bar 来修改传递给 foo 的指针所指向的任何内容。 - Praetorian
这个问题似乎适合在codereview.se上进行。 - Bruno Ferreira
我不会在foo()内部更改bar,这是问题的关键。 我的理解是按值捕获将使指针成为const。 但是,在lambda表达式中没有办法使数据成为const。 - user870130
2
使用[bar] (const Bar* element)作为lambda函数,以及void foo(const Bar* bar)。现在两者都是常量。 - Bruno Ferreira
显示剩余5条评论
2个回答

11

然而,指向的数据是可以改变的,并且无法更改这一点,因为在lambda捕获中不允许使用const。

不,当在lambda表达式中按值捕获时,const性质是被保留的,即捕获指向const数据的指针将防止在lambda内部更改数据。

int i = 1;
const int* ptr = &i;

auto func = [ptr] {
    ++*ptr; // ERROR, ptr is pointer to const data.
}

当按值捕获(lambda表达式)时,Lambda会给指针添加顶层const属性(除非使用mutable)。

auto func = [ptr] {
    ptr = nullptr; // ERROR, ptr is const pointer (const int* const).
}

auto func = [ptr] () mutable { // Mutable, will not add top-level const.
    ptr = nullptr; // OK
}

我可以使用第二个签名,但是无法保护数据在 lambda 表达式中被更改。

你可以通过使用 const 来防止 lambda 内部更改数据。

const Bar* bar = &bar_data;
auto b = [bar] (const Bar* element) { // Data pointed to by bar is read-only.
    return bar == element;
};

同时,lambda表达式需要一个类型为const Bar* const &的参数,即指向常量数据的常量指针的引用。不需要取引用,只需使用const Bar*

有关指针和const的更多信息: const int *,const int * const和int const *有什么区别?


1
你能解释一下你最后一个观点吗?“lambda表达式接受一个类型为const Bar* const &的参数,即对常量指针的常量数据的引用。不需要取一个引用,只需取一个const Bar*即可。” - user870130
2
将指向指针的引用传递仅在您打算从lambda内部修改传递给lambda的指针时才有用。 在您的情况下,您只检查指针相等性,因此const Bar *就足够了。 - Felix Glas

3
你的问题似乎源于对lambda表达式中变量捕获方式的误解。当你通过复制来捕获一个变量时,从lambda表达式生成的闭包类型中所创建的相应数据成员将具有与原始对象相同的类型。这保留了const性质,并且你不能在lambda体内修改bar指向的任何内容。
来自§5.1.2/15 [expr.prim.lambda]
“如果实体被隐式捕获并且捕获默认值为=,或者如果它被显式捕获而不是形式为& identifier或& identifier initializer的捕获,则该实体被复制捕获。对于每个被复制捕获的实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序是未指定的。如果实体不是指向对象的引用,则此类数据成员的类型为相应捕获实体的类型,否则为引用类型。”

{{链接1:演示现场}}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接