7

GCC 4.8 的最新版本在头文件中提供了以下代码:

auto L = [](){};

struct S
{
    decltype(L) m;
};

以下警告:

test.hpp:3:8: warning: 'S' has a field 'S::m' whose type uses the anonymous namespace [enabled by default]
 struct S
        ^

为什么编译器会考虑 lambda 的类型来使用匿名命名空间?我将 lambda 设为全局,我没有在任何地方使用匿名命名空间。

更新:即使我将 lambda 放在显式命名空间中,编译也会给出相同的警告,如下所示:

namespace N
{
    auto L = [](){};
}

struct S
{
    decltype(N::L) m;
};

更新 2:事实上,似乎甚至类范围 lambdas 也有同样的问题:

class N
{
    static constexpr auto L = [](){};
};

struct S
{
    decltype(N::L) m;
};
4

4 回答 4

7

§5.1.2/3:

lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。此类类型不是聚合 (8.5.1)。闭包类型在包含相应 lambda 表达式的最小块作用域、类作用域或命名空间作用域中声明。

因此,除非您在匿名命名空间内的代码中定义 lambda 表达式,否则 lambda 的类型也不应该包含在匿名命名空间中。

于 2012-07-18T04:37:49.453 回答
4

除非我错过了什么,否则这些都不应该在匿名命名空间中,尽管至少 GCC 和 MSVC 似乎都将它们放在那里。

§5.1.2 [expr.prim.lambda] p3

[...] 闭包类型在包含相应lambda-expression的最小块范围、类范围或命名空间范围中声明。[...]

至少 Clang 似乎做对了,闭包类型位于应有的位置。

(您可以通过在某种警告/错误生成代码中简单地包含 lambda 来测试 lambda 类型驻留在哪个命名空间中。编译器应将其类型与警告/错误一起吐出。)

于 2012-07-18T04:41:20.520 回答
1

GCC 的警告可能有点令人困惑,但它的意图肯定是正确的。lambda 的类型是未命名的,它在整个程序中是唯一的。另一方面,如果您的类没有放在未命名的命名空间中(根据您的描述,我想它不是),那么您的类在您包含它的每个翻译单元中都是相同的类型。由于同一个类应该具有相同的成员,而不是不同翻译单元中的不同成员,因此这是一种违规(并导致未定义的行为)。

至少同样糟糕的是,L一旦extern将标头包含到多个翻译单元中,您将获得“L 的多个定义”链接器错误。

于 2012-07-18T22:22:28.277 回答
0

在这种情况下,我没有阅读规范,只是想......但是,你能做一个测试吗?

您的 lambda 太琐碎,可能会变成无状态的 lambda。像你这样的无状态 lambda 可以通过编译器变成一个简单的 C 函数。可能有一些规则可以将此函数放在匿名命名空间中,以便它们仅存在于单个编译单元中。

所以我建议你让它成为非无状态的,比如引用一个变量,看看它是否仍然存在于这个匿名命名空间中。

于 2012-07-18T04:43:31.987 回答