【问题标题】:g++ signature/symbol : no difference between static and non-static member function?g++ 签名/符号:静态和非静态成员函数之间没有区别吗?
【发布时间】:2014-04-25 14:49:23
【问题描述】:

以不同方式定义相同类 A 的两个库(这是 legacy-crap-code)

A 的原型:

在库 A 中:

#include <string>

struct A
{
     static void func( const std::string& value);
};

在库 B 中:

#include <string>

struct A
{
   void func( const std::string& value);
};

main.cpp 使用来自 lib A(组件 A)的 A:s 标头

#include "liba.h"

int main()
{
    A::func( "some stuff");
    return 0;
}

main 与 lib A 和 lib B 链接。

如果 lib B 在 lib A 之前“链接”(在链接指令中),我们会得到一个核心,因此,lib B:s 定义是纠察队。

这不是我预期的行为。我认为符号之间会有一些差异,因此加载器/运行时链接器可以选择正确的符号。也就是说,非静态成员函数的隐藏 this 指针以某种方式包含在符号中。

这真的是符合规范的行为吗?

两者的行为相同:

g++ (GCC) 4.4.7 20120313(红帽 4.4.7-4)

带有 g++ 4.8.1 的 RHEL 开发工具

【问题讨论】:

  • 像你一样,我本来希望成员函数的静态性以某种方式编码在损坏的名称中,只是为了避免此类错误。但在 GCC 中,显然不是。
  • “这真的是符合标准的行为吗?” 你的程序违反了单一定义规则,所以它不是一个有效的 C++ 程序,所以是的,它 100% 符合你的程序以意想不到的方式打破。

标签: c++ g++


【解决方案1】:

不可能用静态成员函数重载非静态成员函数,反之亦然。来自标准:

ISO 14882:2003 C++ 标准 13.1/2 – 可重载声明

某些函数声明不能 重载:

  • 仅返回类型不同的函数声明不能​​被重载。
  • 如果其中任何一个是static 成员函数声明 (9.4),则不能重载具有相同名称和相同参数类型的成员函数声明。

更多细节和参考可以在问题5365714中找到。

所以你在同一个程序中有同一个类A的两个定义,它们应该是相同的,而它们不是。 在单独的翻译单元中存在不一致的定义时发出错误信号对于链接器来说不是强制性的。 结果是实现定义的程序格式错误(根据@jonathan 的评论更新)。在 Stroustrup 在C++ faq 中的一个说明性示例中,它被描述为未定义的行为。

在 GCC 的情况下,如您所说,使用的定义取决于链接命令中库的顺序(假设 lib A 和 lib B 自行编译,然后与主程序链接)。链接器使用从左到右传递的库中的第一个定义。 关于 GCC 的链接顺序选项的讨论在 409470

【讨论】:

  • 很好的答案,只是它不是“实现定义的”,它是格式错误的,不需要诊断。实现定义意味着实现需要记录发生的情况,但程序已损坏,因此对实现没有要求。
  • @joanpau 谢谢。我想我应该更清楚我知道它不是符合标准的代码,我在问从链接器/加载器的角度来看是否可以产生这种行为(给定废话代码)。我希望链接器/加载器需要以某种方式提供帮助,或者至少“做正确的事”。有人可能会争辩说,核心是最好的结果,而我是伸出援助之手......
  • 正如@jonathan 正确指出的那样,该程序格式错误(答案已更新)。在这种情况下实现应该做什么的概念尚不清楚(1580539422180312),但没有要求:如果链接器检测到不一致,则可以报告错误,或者生成一个意外的程序结果就像你的情况一样。
【解决方案2】:

你不能在 C++ 中基于返回类型重载函数,所以我猜你不能基于静态/非静态成员函数来重载。

您将需要修复其中一个头文件——最好不要两次声明相同的类型。

为了说明看这个;

struct A {
    int X(int b);
};
int A::X(int b)
{
    return b+8;
}

$ g++ x.cc -c
$ nm x.o
0000000000000000 T _ZN1A1XEi

并将其与此进行比较....

struct A {
    static int X(int b);
};
int A::X(int b)
{
    return b+8;
}

$ g++ x.cc -c
$ nm x.o
0000000000000000 T _ZN1A1XEi

观察两件事;

  1. 当我声明 A::X 的实际实现时,我没有指定它是一个静态成员函数 -- 编译器并不关心,而是从 struct 的定义中获取任何信息。
  2. 符号的名称修饰(无论是否为静态)都与 _ZN1A1XEi 相同,它对类名称、方法名称和参数类型进行编码。

总之,对编译后的代码使用不正确的标头会导致未定义的行为......

【讨论】:

  • 返回类型相同。问题是一个是静态的,另一个是非静态的。而且,是的,我可以想出几种方法来解决这个问题,但我现在想知道这是否是预期的行为。
  • 我在答案中添加了更多信息——底线是你应该期待未定义的行为。我很惊讶你在定义同一个结构两次时没有得到任何编译错误,我只能假设一些条件编译或有两个版本的头文件意味着每个编译单元实际上只有一个定义——但是当你从我的示例中可以看出,符号表不编码任何静态性质的信息——但生成调用代码会,因为 *this 没有提供给静态成员,如果预期你会得到一个核心/崩溃
  • 请注意,有时函数的返回类型包含在损坏的名称中(主要是模板函数):mentorembedded.github.io/cxx-abi/abi.html#mangling 在尝试阅读该规范时,我肯定会为任何人感到抱歉实施它。
  • 正如我所描述的,A 类的两个 TU:s 链接在两个不同的库中。所以,我们没有违反单一定义规则,至少是编译时间。符号表来自我也检查过的 g++,并且是我们正在使用的实现 - 显然这在 g++ 上是预期的:)。我的问题是,这是否符合标准(或标准的意图,因为据我所知,标准中未指定链接等)。
【解决方案3】:

由于一个类不能同时具有同名的静态成员函数和非静态成员函数,因此不需要在重命名的名称中包含该信息。

您需要通过为您的类添加命名空间、重命名它们或注意不要将这些库一起使用来解决此问题。

【讨论】:

  • 我假设您的意思是具有相同的签名。显然需要复制粘贴废话代码:)
猜你喜欢
  • 2012-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-14
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多