全局作用域Lambda与ODR的配合机制及重载函数foo的链接性问题
全局作用域Lambda与ODR的配合机制及重载函数foo的链接性问题
咱们先拆解你这段C++20代码里的核心问题哈,先从最关键的lambda特性说起:
- 每个lambda表达式都是独一无二的类型载体:哪怕你在全局作用域写了两个完全一模一样的
[](){},它们对应的闭包类型也是完全不同的——这是C++标准明确规定的,不管lambda的代码逻辑、捕获列表是否完全一致,只要是两个独立的lambda表达式,就会生成不同的闭包类型。
回到你的代码里:
template <typename T, typename F> struct unique {}; template <auto F> struct ltype { using type = decltype(F); }; using p = unique<int, ltype<[](){}>::type>; using q = unique<int, ltype<[](){}>::type>; void foo(p x); void foo(q x);
这里的两个ltype<[](){}>::type,分别对应两个不同的闭包类型,所以p和q其实是两个完全不同的unique模板实例化类型——因为unique的第二个模板参数(闭包类型)不一样。
接下来聊foo函数的重载和链接性:
- 重载的合法性:
void foo(p x);和void foo(q x);是两个合法的重载函数,因为它们的参数类型p和q是不同的,函数签名自然不同,完全符合C++的重载规则,不存在重定义的冲突。 - 链接性分析:
- 首先,全局作用域的lambda闭包类型,在C17及以后拥有外部链接性(你的代码用了C20的
auto非类型模板参数,默认是C++20环境)。 - 模板
unique是全局作用域的模板,实例化出来的p和q类型,因为模板参数(闭包类型)具有外部链接性,所以这两个类型也拥有外部链接性。 - 全局作用域声明的
foo函数,默认是外部链接性(没有static修饰),所以这两个重载的foo函数,都会被视为外部可见的函数,只要你在某个源文件中分别提供它们的定义,就不会违反ODR(一次定义规则)。
- 首先,全局作用域的lambda闭包类型,在C17及以后拥有外部链接性(你的代码用了C20的
最后再提ODR的注意点:
- 不要试图把
p和q当成同一个类型使用,比如p a; q b = a;这种代码会直接编译失败,因为它们本质是不同的类型。 - 如果你的代码中只声明了这两个
foo重载却没有定义,那么在调用它们的时候会触发“未定义引用”的链接错误,这是正常的——因为链接器找不到对应的函数实现。
内容来源于stack exchange
相关产品推荐
相关产品推荐

