我认为以下代码违反了ODR(foo.cpp和main.cpp中都定义了f
),但编译器没有产生错误。
// foo.h
auto f = [](int i){return i;};
void printTypeID();
// foo.cpp
#include "foo.h"
#include <typeinfo>
#include <iostream>
void printTypeID(){
std::cout << typeid(f).name() << '\n';
}
// main.cpp
#include "foo.h"
#include <typeinfo>
#include <iostream>
int main(){
std::cout << typeid(f).name() << '\n';
printTypeID();
return 0;
}
输出typeid是相同的,这让我感到惊讶,因为我认为每个lambda表达式都有一个唯一的非联合类类型,除非它们在外部内联函数定义的主体中。
编辑:当我查看标准时,我打算认为这是因为未命名的类类型的对象具有内部链接。虽然我没有找到相应的规范。这些是我在下面找到的相关规范。
lambda表达式的类型是一个唯一的,未命名的非联合类类型:expr.prim.lambda
lambda-expression的类型(也是闭包对象的类型)是一个唯一的,未命名的非联合类类型 - 称为闭包类型 - 其属性如下所述。 [...]
具有全局范围的未命名类类型没有链接,但是,仍然可以使用没有链接的类型来声明具有外部链接的对象。 basic.link
这些规则未涵盖的名称没有链接。此外,除非另有说明,否则在块作用域([basic.scope.block])中声明的名称没有链接。 类型被认为具有链接当且仅当:
- 它是一个名为(或具有连接目的名称([dcl.typedef]))的类或枚举类型,并且名称具有链接;或
- 它是一个未命名的类或未命名的枚举,它是具有链接的类的成员;或
- 它是类模板的特化(Clause [temp])34;或
- 它是一种基本类型([basic.fundamental]);或
- 它是除类或枚举之外的复合类型([basic.compound]),仅由具有链接的类型组合而成;或
- 它是具有链接的类型的cv限定版([basic.type.qualifier])。
除非
,否则不带链接的类型不能用作具有外部链接的变量或函数的类型
- 该实体具有C语言链接([dcl.link])或
- 实体在未命名的命名空间([namespace.def])或
中声明- 该实体未使用过频繁([basic.def.odr])或在同一个翻译单元中定义。
lambda表达式的对象(本例中为f
)不是根据以下引文具有内部链接的变量:basic.link
具有命名空间作用域的名称([basic.scope.namespace])如果名称为
,则具有内部链接
- 显式声明为static的变量,函数或函数模板;或者,
- 非易失性const限定类型的非内联变量,既未显式声明为extern,也未声明为具有外部链接;或
- 匿名工会的数据成员。
未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间具有内部链接。所有其他名称空间都有外部链接。如果名称为[...]
,则具有未在上面给出内部链接的命名空间范围的名称与封闭命名空间具有相同的链接
因此,先前代码中的输出typeid可能是相同的,并且这不是问题,因为在这种情况下闭包类型没有链接。但问题是:
具有全局范围的未命名类类型的对象是否具有内部链接?