问题标签 [one-definition-rule]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - 优化 ODR 使用的空类
当今的许多 C++ 代码往往在最大程度上是模板加载的。它们是库:STL、Boost.Spirit、Boost.MPL 等等。他们鼓励用户以 form 声明功能对象
struct S { /* presence of non-virtual member functions and operators, but absense of non-static data members or non-empty base classes */ }; S const s{};
。它们中的大多数是无状态的(即static_assert(std::is_empty< S >{});
持有)。对于那些使用 ODR 的那些,无论data
文件的空部分增长 1 个字节(sizeof(S) == 1
对于空类型S
,因为随后分配的对象的所有地址都应该不同)。即使在 Boost.Spirit 的简单语法中,也有很多这样的 ODR 使用的空类。但是为他们保留空间是绝对没有意义的。
我尝试使用以下代码( )clang
对coliru-Ofast
进行测试:
并得到结果(size
实用程序的输出DIM == 100
,即 100 * 100 个类):
如果我将签名更改为diff(lhs & l, rhs & r)
以diff(lhs l, rhs r)
禁止使用 ODR,则结果为:
几乎等于(data
仅感兴趣的部分)assert((check< DIM >()));
行的简单注释的情况(部分的大部分text
是可预测的 DCE 优化出来的):
因此,我得出结论,对于 ODR 使用的空类没有优化。
对于显式指定的模板参数,可以使用简单的类型过滤器:
但是在我看来,推导的模板类型没有简单的解决方法。
现代编译器中是否隐含上述优化?如果是,如何启用它?如果没有,目前是否有一种技术可以实现所需的行为?
我知道有时对象的地址会咕哝很多,但在上述情况下并非如此。
我认为变量或类型的属性(例如[[immaterial]]
)会很方便。也许这样的属性(用于类)应该拒绝获取属性类实例地址的可能性(编译时硬错误)或地址运算符&
应该返回无意义的值(实现定义)。
c++ - 在类中静态常量 ODR
我static
对成员的类内初始化有点困惑const
。例如,在下面的代码中:
我没有定义Foo::n
(该行已注释)。所以,我希望调用f(Foo::n)
在链接时失败,确实如此。但是,std::cout << g(Foo::n) << std::endl;
每当我使用优化标志(例如-O1/2/3
.
- 为什么打开优化时gcc(用 gcc5.2.0 和 gcc 4.9.3 尝试过)编译和链接代码?
- 我是否正确地说类内静态 const 成员的唯一用法是常量表达式,例如
h<Foo::n>
调用中的模板参数,在这种情况下代码应该链接?
c++ - 多个翻译单元中函数模板实例化的标识
根据cppref,多个翻译单元中函数的身份特征inline
如下:
...
2)它在每个翻译单元中具有相同的地址。
3)所有函数定义中的函数局部静态对象在所有翻译单元之间共享(它们都引用在一个翻译单元中定义的同一个对象)
...
简单地说,隐含了单例身份。
我想知道这是否同样适用于没有说明符的函数模板实例化inline
。
c++ - 模板结构本身违反了 ODR
今天我第一次编译了我的代码库,并使用 gcc 5.1 打开了链接时间优化,我收到了奇怪的 ODR 警告:
代码如下所示:
如您所见,Func 与自身发生冲突。
gcc 如何在模板类中找到 ODR 问题?我自己并不完全理解 ODR,但我觉得应该有“多重声明”错误。
注意:成员 f 似乎是问题的根源:
注意 2:问题似乎只在我在析构函数中构造 Functor 时出现:
异步看起来像这样:
奇怪的是,函子没有在析构函数中声明,不会产生警告。我觉得 gcc 正在生成这些导致冲突的 2 个版本。
c++ - 同名的 C++ 内联函数和外部函数给出了意想不到的结果
以下代码不违反一个定义规则,但它给出了意想不到的结果:
测试.hpp
测试1.cpp
测试2.cpp
主文件
我期待它打印“1 99”,但它总是打印“1 1”。
关于 Test::test 的两个定义,因为其中一个是内联定义,所以也不违反一个定义规则。
所以这个程序是有效的,但它没有打印出预期的结果......
这个程序有什么问题吗?还是我对 ODR 规则有误解?(参考 C++ 标准会有所帮助)。
c++ - MSVC 中的 ODR 错误?
此程序在使用 MSVC 编译时打印1 1
而不是打印1 2
(直到 VS 2015)。
f1.cpp:
f2.cpp:
主.cpp:
就好像 的不同定义F
正在破坏 ODR。但是本地类不应该是不同的吗?有趣的是,如果我们F
用 lambda 函数替换,则不会发生冲突。
那么这是一个编译器错误还是我误解了一个定义规则?
c++ - 内联函数编译
我打算为操作系统 API 提供简单的包装器,当错误发生时会抛出异常。这些包装器很简单,并且都被定义为头文件中的内联函数。由于系统 API 应该很大,因此头文件也应该很大,包含大量微小的内联函数。问题是,如果在包含头文件的情况下编译共享库(.so),所有这些微小的包装器会被编译成生成的二进制文件,从而导致一个大的二进制文件,即使实际上只有一小部分包装器是用过的?可执行文件的情况如何,会有所不同吗?如果是这种情况,将包装器拆分为多个头文件是唯一的解决方案吗?或者我应该通过指定使包装器内部链接static
?
这是我的想法。包装器可以被 ODR 使用(例如,获取其地址)。而在Linux 平台上,默认导出具有外部链接的函数(即可以被其他二进制模块链接)。所以我想链接器可能有必要为它们实际生成大纲定义。请参阅此处描述部分中的要点 3) 。
包装CloseHandle()
在 Windows API 中的简单示例:
c++ - C++中虚函数的编译时静态类型检查
背景
最近,我的一位同事遇到了一个问题,即使用了旧版本的库头文件。结果是为调用C++ 中的虚函数而生成的代码引用了类的虚函数查找表( vtable)中的错误偏移量。
不幸的是,在编译过程中没有发现这个错误。
问题
所有普通函数都使用它们的重命名链接,以确保链接器选择正确的函数(包括正确的重载变体)。同样,可以想象一个目标文件或库可以包含有关C++ 类的vtable中的函数的符号信息。
有没有办法让 C++ 编译器(比如g++
或 Visual Studio)在链接期间检查对虚函数的调用?
例子
这是一个简单的测试示例。想象一下这个简单的头文件和相关的实现:
基础.hpp:
派生的.cpp:
现在,假设一个程序使用了错误/旧版本的头文件,其中缺少中间的虚函数:
BaseWrong.hpp
所以这里我们有主程序:
主文件
当我们使用正确的头文件编译“库” ,然后使用错误的头文件编译和链接主程序时……</p>
…然后虚拟调用使用vtableh()
中的错误索引,因此调用实际上转到:g()
在这个小例子中,函数g()
和h()
具有相同的签名,因此“唯一”出错的是调用了错误的函数(这本身很糟糕,可能完全被忽视),但如果签名不同,则可以(并且有看到)导致堆栈损坏 - 例如,当在 Windows 上调用使用 Pascal 调用约定的 DLL 中的函数时(调用者推送参数,被调用者在返回之前弹出它们)。
c++ - 静态 const 积分成员的基本乐趣
考虑以下代码:
如果不打开您最喜欢的编译器,您认为尝试编译和链接这个简单的野兽会产生什么结果?
有些人可能会感到惊讶,它不仅取决于编译器,还取决于它的优化选项!例如,在 gcc 上,代码将拒绝在优化关闭的情况下链接,但会很乐意在任何优化打开的情况下链接(并生成可运行的无操作可执行文件)。
失败案例中的诊断会很有趣 -X::i
找不到符号。启用优化的链接会成功,因为X::i
会被丢弃。
和问题。编译此代码的编译器行为是否正确?由于X::i
没有链接,当被要求生成一个要求该符号链接的代码时,编译器不应该抱怨吗?
c++ - 纯虚函数实现
我正在阅读 Scott Meyers 的Effective C++,我在关于继承的部分。他说过
纯虚函数仅指定接口的继承。
简单(不纯)虚函数指定接口继承加上默认实现的继承。
现在,考虑以下两个类:
我们仍然可以像我在示例中所做的那样为纯虚函数提供默认实现,并通过qualified-function-call-expression
. 我们可以对不纯的虚函数做几乎相同的事情
如果我不定义不纯虚函数如下:
它会工作得很好。
那么,纯虚函数的唯一目的是使类抽象(不可实例化)?