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成为全局的,我没有在任何地方使用匿名命名空间。
UPDATE:即使我将lambda放在显式命名空间中,编译也会发出相同的警告,如下所示:
namespace N
{
auto L = [](){};
}
struct S
{
decltype(N::L) m;
};
UPDATE 2:事实上,似乎即使是类范围的lambdas也有同样的问题:
class N
{
static constexpr auto L = [](){};
};
struct S
{
decltype(N::L) m;
};
§5.1.2/3:
lambda表达式的类型(也是闭包对象的类型)是一个唯一的、未命名的非连接类类型——称为闭包类型——其属性如下所述。此类类型不是聚合(8.5.1)。闭包类型在最小的块作用域、类作用域或命名空间中声明包含相应lambda表达式的作用域。
因此,除非在匿名命名空间内的代码中定义lambda表达式,否则lambda的类型也不应包含在匿名命名空间中。
除非我遗漏了什么,否则这些都不应该在anonyus命名空间中,尽管GCC和MSVC似乎都把它们放在了那里。
§5.1.2 [expr.prim.lambda] p3
[…]闭包类型是在最小的块作用域、类作用域或命名空间中声明的包含相应lambda表达式的作用域。[…]
Atleast Clang似乎做对了,闭包类型位于它应该位于的位置。
(您可以通过简单地将lambda包含在某种产生警告/错误的代码中来测试lambda类型驻留在哪个命名空间中。编译器应该将其类型与警告/错误一起吐出。)
GCC的警告可能有点令人困惑,但其意图肯定是正确的。lambda的类型是未命名的,并且在整个程序中是唯一的。另一方面,如果你的类没有放在一个未命名的命名空间中(根据你的描述,我想不是这样),那么你的类在你包含它的每个翻译单元中都是相同的类型。由于同一个类应该有相同的成员,而不是在不同的翻译单元中有不同的成员,这是一种违规行为(并导致未定义的行为)。
至少同样糟糕的是,L
就是extern
,因此一旦将标头包含到多个翻译单元中,就会出现"L的多个定义"链接器错误。
在这种情况下,我没有阅读规范,只是想。。。,但是,你能做个测试吗?
您的lambda太琐碎,可能会变成无状态lambda。像您这样的无状态lambda可以通过编译器变成一个简单的C函数。可能有一些规则可以将这些函数放在匿名命名空间中,以便它们只存在于单个编译单元中。
因此,我建议您使它成为非无状态的,就像引用一个变量一样,看看它是否仍然存在于这个匿名命名空间中。