源文件中的typedef/类型别名是否会引发ODR违反?
问题
我习惯把源文件里定义的类型放在匿名命名空间中,因为知道这类定义可能会导致ODR违反:
// my_source.cpp namespace { struct MyStruct {}; }
但如果源文件里没有定义类型,只是给已有类型创建别名,比如:
// another_source.cpp using error_t = ::prj::telemetry::ErrorType;
如果另一源文件里有这样的代码:
// some_other_source.cpp using error_t = ::prj::linalg::ErrorType;
这种情况会不会造成ODR违反或者异常链接行为(比如丢弃其中一个类型)?简单说,适用于类型定义的规则是否同样适用于typedef?
回答
一句话先给结论:typedef/类型别名和类型定义是两码事,ODR规则管不到别名,你说的这种情况完全不会有ODR违反或链接问题。
具体拆解:
- 先搞懂ODR管的是什么:它约束的是需要分配存储空间、有具体实现的实体,比如类、函数、全局变量这些。而
typedef或者C++11的using别名,说白了就是给已有类型贴个“别名标签”,它本身不是新类型,也不会生成独立的符号——编译的时候,别名直接会被替换成它指向的原类型,链接阶段根本看不到它的存在。 - 你例子里的两个
error_t,各自在自己的源文件里指向不同类型,但它们都是文件作用域的别名,只在当前源文件有效。编译时每个源文件的代码用各自的error_t对应的原类型处理,链接的时候这俩别名不会产生任何冲突,因为它们根本没出现在目标文件的符号表中。 - 对比你第一个例子里的
struct MyStruct:这是实打实的类型定义,默认有外部链接属性,如果多个源文件都定义同名的MyStruct,链接器就会报重复符号错误——这才需要用匿名命名空间把它改成内部链接,让每个源文件的MyStruct都是独立的。但别名不存在这个问题,因为它不是定义,只是引用。 - 至于“丢弃其中一个类型”的情况更不可能:链接阶段只会处理
::prj::telemetry::ErrorType和::prj::linalg::ErrorType这两个原类型的符号,只要这两个类型本身的定义符合ODR规则(比如各自只定义一次,或者是头文件里的inline类/模板),就完全没问题。两个源文件的别名只是各自用各自的原类型,互相不干扰。
总结一下:适用于类型定义的ODR规则,对typedef/类型别名完全不适用,你说的场景不会有任何链接问题。
内容的提问来源于stack exchange,提问作者Lorah Attkins
相关产品推荐
相关产品推荐

