提问人:ThreeStarProgrammer57 提问时间:2/17/2022 最后编辑:ThreeStarProgrammer57 更新时间:2/17/2022 访问量:498
在这种情况下,如何防止违反ODR规定?
How to prevent ODR violations in this case?
问:
免责声明:此问题是关于防止意外的命名冲突,并确保以下代码无法编译/链接。
[编辑] 实际上,我会很高兴能够阻止编译/链接,或者解决此问题的东西,例如匿名命名空间。但是匿名命名空间不应该放在标头中。
// Class1.h
// --------
namespace SomeLargeLib {
struct S {
int a;
S() { a = 1; }
};
}
// Class1.cpp
// ----------
#include "Class1.h"
void foo() { SomeLargeLib::S s; }
// Class2.h
// --------
namespace SomeLargeLib {
struct S {
int a;
S() { a = 2; }
};
}
// Class2.cpp
// -----------
#include "Class2.h"
int main() {
SomeLargeLib::S s;
return s.a; // returns 1 !!
}
这里发生的情况是,ctor S::S 有两个内联定义,因此允许链接器假设所有定义都相同并选择任何一个。请注意,将 S::S ctor 移到类之外会导致链接器错误,因为它们不再是内联的。
无论如何,既然我们不应该在标头中使用匿名命名空间(请参阅 C++ 核心指南),那么应该做些什么来防止此类 ODR 违规?链接器错误是完美的,但标准规定不需要对 ODR 违规进行诊断。
答:
2赞
Guillaume Racicot
2/17/2022
#1
ODR 违规极难自动检测,这就是为什么它格式不佳,无需诊断。
但是,如果您的目标是预防和检测此类情况,只需使用具有强大所有权的模块即可。该模型完全防止了大多数(如果不是全部)难以检测的 ODR 违规行为。
例:
class1.ixx
:
export module class1;
namespace SomeLargeLib {
export struct S {
int a;
S() { a = 1; }
};
}
class2.ixx
:
export module class2;
namespace SomeLargeLib {
export struct S {
int a;
S() { a = 2; }
};
}
test1.cpp
:
import class1;
import <iostream>;
void fun() {
SomeLargeLib::S a;
std::cout << a.a; // 1
}
test2.cpp
:
import class2;
import <iostream>;
void fun() {
SomeLargeLib::S a;
std::cout << a.a; // 2
}
最棒的是,您可以在同一个二进制文件中链接和连接在一起。从未发生过碰撞。test1.cpp
test2.cpp
但是,test3 会发出编译器错误:test3.cpp
import class1;
import class2; // error: name SomeLargeLib::S already exists, can't import both
目前,MSVC 已支持这些用例。
评论
0赞
ThreeStarProgrammer57
2/17/2022
很好,我会研究模块,谢谢!
评论
Point