问题标签 [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.

0 投票
1 回答
111 浏览

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 使用的空类。但是为他们保留空间是绝对没有意义的。

我尝试使用以下代码( )clangcoliru-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]])会很方便。也许这样的属性(用于类)应该拒绝获取属性类实例地址的可能性(编译时硬错误)或地址运算符&应该返回无意义的值(实现定义)。

0 投票
4 回答
1413 浏览

c++ - 在类中静态常量 ODR

static对成员的类内初始化有点困惑const。例如,在下面的代码中:

Live example

我没有定义Foo::n(该行已注释)。所以,我希望调用f(Foo::n)在链接时失败,确实如此。但是,std::cout << g(Foo::n) << std::endl;每当我使用优化标志(例如-O1/2/3.

  1. 为什么打开优化时gcc(用 gcc5.2.0 和 gcc 4.9.3 尝试过)编译和链接代码?
  2. 我是否正确地说类内静态 const 成员的唯一用法是常量表达式,例如h<Foo::n>调用中的模板参数,在这种情况下代码应该链接?
0 投票
1 回答
500 浏览

c++ - 多个翻译单元中函数模板实例化的标识

根据cppref,多个翻译单元中函数的身份特征inline如下:

...

2)它在每个翻译单元中具有相同的地址。

3)所有函数定义中的函数局部静态对象在所有翻译单元之间共享(它们都引用在一个翻译单元中定义的同一个对象)

...

简单地说,隐含了单例身份。

我想知道这是否同样适用于没有说明符的函数模板实例化inline

0 投票
0 回答
397 浏览

c++ - 模板结构本身违反了 ODR

今天我第一次编译了我的代码库,并使用 gcc 5.1 打开了链接时间优化,我收到了奇怪的 ODR 警告:

代码如下所示:

如您所见,Func 与自身发生冲突。

gcc 如何在模板类中找到 ODR 问题?我自己并不完全理解 ODR,但我觉得应该有“多重声明”错误。

注意:成员 f 似乎是问题的根源:

注意 2:问题似乎只在我在析构函数中构造 Functor 时出现:

异步看起来像这样:

奇怪的是,函子没有在析构函数中声明,不会产生警告。我觉得 gcc 正在生成这些导致冲突的 2 个版本。

0 投票
1 回答
193 浏览

c++ - 同名的 C++ 内联函数和外部函数给出了意想不到的结果

以下代码不违反一个定义规则,但它给出了意想不到的结果:

测试.hpp

测试1.cpp

测试2.cpp

主文件

我期待它打印“1 99”,但它总是打印“1 1”。

关于 Test::test 的两个定义,因为其中一个是内联定义,所以也不违反一个定义规则。

所以这个程序是有效的,但它没有打印出预期的结果......

这个程序有什么问题吗?还是我对 ODR 规则有误解?(参考 C++ 标准会有所帮助)。

0 投票
1 回答
361 浏览

c++ - MSVC 中的 ODR 错误?

此程序在使用 MSVC 编译时打印1 1而不是打印1 2(直到 VS 2015)。

f1.cpp:

f2.cpp:

主.cpp:

就好像 的不同定义F正在破坏 ODR。但是本地类不应该是不同的吗?有趣的是,如果我们F用 lambda 函数替换,则不会发生冲突。

那么这是一个编译器错误还是我误解了一个定义规则?

0 投票
1 回答
1277 浏览

c++ - 内联函数编译

我打算为操作系统 API 提供简单的包装器,当错误发生时会抛出异常。这些包装器很简单,并且都被定义为头文件中的内联函数。由于系统 API 应该很大,因此头文件也应该很大,包含大量微小的内联函数。问题是,如果在包含头文件的情况下编译共享库(.so),所有这些微小的包装器会被编译成生成的二进制文件,从而导致一个大的二进制文件,即使实际上只有一小部分包装器是用过的?可执行文件的情况如何,会有所不同吗?如果是这种情况,将包装器拆分为多个头文件是唯一的解决方案吗?或者我应该通过指定使包装器内部链接static

这是我的想法。包装器可以被 ODR 使用(例如,获取其地址)。而在Linux 平台上,默认导出具有外部链接的函数(即可以被其他二进制模块链接)。所以我想链接器可能有必要为它们实际生成大纲定义。请参阅此处描述部分中的要点 3) 。

包装CloseHandle()在 Windows API 中的简单示例:

0 投票
1 回答
569 浏览

c++ - C++中虚函数的编译时静态类型检查

背景

最近,我的一位同事遇到了一个问题,即使用了旧版本的库头文件。结果是为调用C++ 中的虚函数而生成的代码引用了类的虚函数查找表( vtable)中的错误偏移量。

不幸的是,在编译过程中没有发现这个错误。

问题

所有普通函数都使用它们的重命名链接,以确保链接器选择正确的函数(包括正确的重载变体)。同样,可以想象一个目标文件或库可以包含有关C++ 类的vtable中的函数的符号信息。

有没有办法让 C++ 编译器(比如g++或 Visual Studio)在链接期间检查对虚函数的调用?

例子

这是一个简单的测试示例。想象一下这个简单的头文件和相关的实现:

基础.hpp:

派生的.cpp:

现在,假设一个程序使用了错误/旧版本的头文件,其中缺少中间的虚函数:

BaseWrong.hpp

所以这里我们有主程序:

主文件

当我们使用正确的头文件编译“库” ,然后使用错误的头文件编译和链接主程序时……</p>

…然后虚拟调用使用vtableh()中的错误索引,因此调用实际上转到:g()

在这个小例子中,函数g()h()具有相同的签名,因此“唯一”出错的是调用了错误的函数(这本身很糟糕,可能完全被忽视),但如果签名不同,则可以(并且有看到)导致堆栈损坏 - 例如,当在 Windows 上调用使用 Pascal 调用约定的 DLL 中的函数时(调用者推送参数,被调用者在返回之前弹出它们)。

0 投票
1 回答
118 浏览

c++ - 静态 const 积分成员的基本乐趣

考虑以下代码:

如果不打开您最喜欢的编译器,您认为尝试编译和链接这个简单的野兽会产生什么结果?

有些人可能会感到惊讶,它不仅取决于编译器,还取决于它的优化选项!例如,在 gcc 上,代码将拒绝在优化关闭的情况下链接,但会很乐意在任何优化打开的情况下链接(并生成可运行的无操作可执行文件)。

失败案例中的诊断会很有趣 -X::i找不到符号。启用优化的链接会成功,因为X::i会被丢弃。

和问题。编译此代码的编译器行为是否正确?由于X::i没有链接,当被要求生成一个要求该符号链接的代码时,编译器不应该抱怨吗?

0 投票
1 回答
1207 浏览

c++ - 纯虚函数实现

我正在阅读 Scott Meyers 的Effective C++,我在关于继承的部分。他说过

纯虚函数仅指定接口的继承。

简单(不纯)虚函数指定接口继承加上默认实现的继承。

现在,考虑以下两个类:

我们仍然可以像我在示例中所做的那样为纯虚函数提供默认实现,并通过qualified-function-call-expression. 我们可以对不纯的虚函数做几乎相同的事情

如果我不定义不纯虚函数如下:

它会工作得很好。

演示

那么,纯虚函数的唯一目的是使类抽象(不可实例化)?