在这种情况下,如何防止违反ODR规定?

How to prevent ODR violations in this case?

提问人:ThreeStarProgrammer57 提问时间:2/17/2022 最后编辑:ThreeStarProgrammer57 更新时间:2/17/2022 访问量:498

问:

免责声明:此问题是关于防止意外的命名冲突,并确保以下代码无法编译/链接。

[编辑] 实际上,我会很高兴能够阻止编译/链接,或者解决此问题的东西,例如匿名命名空间。但是匿名命名空间不应该放在标头中。

// 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 违规进行诊断。

C++ 单定义规则 cpp-core-guidelines

评论

0赞 Quentin 2/17/2022
您的问题是如何检测此类违规行为,还是如何实现两个同名类?
2赞 Alan Birtles 2/17/2022
只是不要将类放在名称冲突的全局命名空间中?
0赞 ThreeStarProgrammer57 2/17/2022
我不想实现同名类,我只是假设一个大型代码库可能会有这种意外的冲突。例如,一个帮助程序类本应在标头中“本地”使用,但随后另一个标头执行相同的操作,并且恰好位于同一命名空间中。Point
1赞 Alan Birtles 2/17/2022
最好的做法是使用描述性类名和/或命名空间
1赞 ThreeStarProgrammer57 2/17/2022
@DrewDormann是的,如果我的问题不清楚,我很抱歉

答:

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.cpptest2.cpp

但是,test3 会发出编译器错误:test3.cpp

import class1;
import class2; // error: name SomeLargeLib::S already exists, can't import both

目前,MSVC 已支持这些用例。

评论

0赞 ThreeStarProgrammer57 2/17/2022
很好,我会研究模块,谢谢!