【问题标题】:Unused lambda capture warning when capture is actually used实际使用捕获时未使用的 lambda 捕获警告
【发布时间】:2018-09-20 01:44:45
【问题描述】:

这段代码:

void foo(int);
int main() {
    const int i = 0;
    auto l = [i](){foo(i);};
}

(godbolt)

使用clang编译时会发出编译器错误

-std=c++17 -Werror -Wunused-lambda-capture

错误信息是error: lambda capture 'i' is not required to be captured for this use

错误是正确的:i 可以在此处隐式捕获,无需显式捕获。但是,a) 警告命名不当,因为使用了 i,但警告是针对 unused lambda 捕获的,并且 b) 我不希望这是一个错误。我想对实际未使用的 lambda 捕获进行错误处理,但对可能已隐式捕获的已使用显式捕获的变量不会出错。

有没有这样的clang设置?还是我必须使用 pragma diagnostic push/pop 来消除错误?

【问题讨论】:

  • 捕获 is 未使用,因为 lambda 中使用的 i 实际上是封闭范围内的 i,而不是隐式成员 i在 lambda 本身中,因为 i 没有被 odr 使用。
  • 没有默认捕获。 lambda 不会隐式捕获任何内容。

标签: c++ clang


【解决方案1】:

我认为你在这里很不幸。如果我们检查实现此功能的评论[Sema] Add warning for unused lambda captures,我们可以看到关于如何消除警告的讨论被广泛讨论。包括使未使用的警告静音的规范 clang 方法,该方法被强制转换为 void:

我认为不应在此处使用预期警告,因为您在 lambda 中有 (void)fname(如果我在本地测试,我不会收到此警告)。

这确实有效see it live,但在这种情况下感觉很傻。

使用-Wno-unused-lambda-capture,但这对您来说不是一个有利的选择:

我认为如果您在 CXX/ 目录中此补丁修改的所有测试的选项中添加“-Wno-unused-lambda-capture”,该补丁会更整洁。这将避免冗余(无效)使用,并确保(无效)使用不会干扰可能仅在捕获列表中使用的原始意图。

从捕获中省略变量,因为它不是 odr 使用的,但正如指出的那样,这暴露了实现分歧,因为 MSVC 不进行此优化:

如果我从列表中删除 kDelta,它将在没有任何警告的情况下编译 捕获次数:

#include <stdio.h>

int main(void) {
  const int kDelta = 10000001;
  auto g = [](int i)
           {
             printf("%d\n", i % kDelta);
           };
  g(2);
}

但随后 Microsoft C++ 编译器会报错:

error C3493: 'kDelta' cannot be implicitly captured because no default capture mode has been specified

我们可以看到这种情况 live as well 并且确实从捕获中删除 i 确实为 clang 和 gcc 修复了它,但对于 MSVC 却没有。

适用于所有实现的另一个解决方案是显式捕获[i=i],但听起来这也不是一个理想的解决方案 (see it live)。

如果我们可以在这里申请 [[maybe_unused]] 但我们不能。

【讨论】:

  • 实现的分歧是非常不幸的。另见llvm.org/pr34865
  • 我在下面发布了我们的生产实用解决方案,基本上是一个愚蠢的宏。
【解决方案2】:

我对此没有很好的解决方案,但我们已经实施了解决方案。

void foo(int);
int main() {
    const int i = 0;
    auto l = [LAMBDA_CONSTANTS(&i)](){foo(i);};
}

LAMBDA_CONSTANTS 是定义在 LambdaConstants.h 中的宏,我们在需要时包含它。

其中的内容是:

#ifndef LAMBDA_CONSTANTS_H
#define LAMBDA_CONSTANTS_H

/*
    given a lambda that wants to capture two constants

    const auto k1 = 1000;
    const auto k2 = 2000;
    int v = 0;

    auto lambda = [&v, &k1, &k2]() {
        v = k1 * k2;
    }

    Then unfortunately clang will correctly warn about unnecessary captures. And MSVC will fail to compile if you don't capture.

    https://stackoverflow.com/questions/52416362/unused-lambda-capture-warning-when-capture-is-actually-used

    An imperfect solution is to declare the lambda using the LAMBDA_CONSTANTS macro.

    auto lambda = [&v
    LAMBDA_CONSTANTS(&k1, &k2)
    ](){
        v = k1 * k2;
    }

    This should work correctly. Most of the time.

NOTE: There is no comma after the final capture variable before the LAMBDA_CONSTANTS macro. The macro is variadic and will work with 1 or more captures.
*/
#ifndef LAMBDA_CONSTANTS
#if _MSC_VER
#define LAMBDA_CONSTANTS(...) ,__VA_ARGS__
#else 
#define LAMBDA_CONSTANTS(...)
#endif
#endif


#endif // LAMBDA_CONSTANTS_H

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-02
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多