C++ 为什么typedef结构会产生链接故障

C++ 为什么typedef结构会产生链接故障,c++,language-lawyer,C++,Language Lawyer,我有一段代码,看起来像这样 typedef struct { int foo; int bar; void foobar(int, char*); } mystruct; 及 及 所有这些都可以完美地编译、链接和运行。除非它没有。在一个编译器上它工作,在另一个编译器(Android GCC)上它失败,并出现链接错误:未满足引用 如果我这样更改它,它将编译并链接 struct mystruct { int foo; int bar; void foobar(int, ch

我有一段代码,看起来像这样

typedef struct {
  int foo;
  int bar;
  void foobar(int, char*);
} mystruct;

所有这些都可以完美地编译、链接和运行。除非它没有。在一个编译器上它工作,在另一个编译器(Android GCC)上它失败,并出现链接错误:未满足引用

如果我这样更改它,它将编译并链接

struct mystruct {
  int foo;
  int bar;
  void foobar(int, char*);
};
我想我知道为什么,但是我不能很好地解释它,我也不能在标准中找到它。有人能给我解释一下,找到合适的参考资料吗

<>编辑:我认为所有人都很清楚这是C++代码。我给它贴上标签;结构中的函数在C中无效;但是,要清楚的是,文件有CPP扩展,编译器将其视为C++。 编辑:应答者注意到调用是一个文本,因此是常量,但arg是非常量。这不是一个关键因素,因为(a)编译器传递了它(b)链接失败,无论参数类型如何

编辑:我的理论是,这与传递给链接器的匿名结构类型有关,因此单独编译的声明和调用不匹配。似乎这可能不正确,在这种情况下,这可能只是一个微妙的编译器错误


编辑:出于好奇,有人能复制这种行为吗?实际的编译器是最近下载的androidndk,以及随附的任何GCC版本。如果其他编译器没有/没有这个问题,那可能就是答案。

我认为这是一个编译器错误。以下更极端的例子仍然可以:

typedef struct { void f(); } f;
void f::f() { }
从。也就是说,您可以使用typedef名称来定义成员函数,即使该成员函数与typedef名称相同。

C++11,9.3/5

如果成员函数的定义在词汇上超出其类定义,则应使用
运算符通过其类名称限定成员函数名称


在第一个示例中,
mystruct
不是类名。

基本上,结构的自身名称位于{括号之前的第一行。最后一个单词是另一个名称,由typedef重命名

它还需要使用so:

typedef struct mystruct{int a;int b;void foobar(int,char*)} othername;
void mystruct::foobar(int,char*){}

似乎是混合C和C++。首先,如果你想把代码变成C++,那么你肯定必须有:

void foobar(int, const char*);
因为“X”的类型是(const char*),而不是(char*)

第二,如果你希望你的代码是C,那么你不能在结构中使用函数。C中只允许指向函数的指针。所以

typedef struct {
    void (*foo)(void);
} X;
<>你的第一个代码在C++代码中是有效的,但是它可能是你真正想要的不同的东西。

TyPulfStuts{}.a;C++中是一个带有TyPulf的无名称结构。

 struct A {
     void foo();
 }
 typedef A B;
 void B::foo() { }
我不认为这是一个有效的C++构造。删除“a”,你得到了你的代码:

 typedef struct {
     void foo();
 } B;
 void B::foo() { }

typedef中匿名结构的习惯用法是我第一次学习C时经常使用的。例如,以下几行是C中类型的有效描述

typedef struct
{
   char * characters;
   int length;
} String;

如果gcc阻止了这一点,那么gcc是错误的。

这不是关于一个与类同名的成员,“类”是不是一个
typedef匿名结构而不是一个类名。@bolov:这也正是我示例中的类的定义方式。是的,但您的解释强调了与类/typedef同名的方法OP没有任何类似的东西。无论如何,它可能只是我解释你的话。@ Bulo:这是因为该方法证明编译器不能在内部命名结构“代码>代码< f>代码>(或<代码> MyStult)。Irc,这是在最早的C++编译器中完成的。现在我明白了,但这并不清楚。(至少对我来说)从你的回答中。你可以在你的回答中添加这一澄清。我认为这包含在“链接目的”中规则。@mike:我觉得S7.1.3和S8一起说typedef名称是用于此目的的类名。我找不到一个例子。@mAlters:很好。S7.1.3/9说typedef名称是用于链接目的的类名。我开始认为这可能是一个编译器错误,实际上并不是正确的行为。如果你声称需要,请ase提供了规定它必须的规则。很明显,你的版本是允许的,但不清楚原始形式是否被禁止(如果是,为什么)这是一个我没有想到的有用的实验线。它仍然不能解释为什么代码编译但不能链接。有很多编译器标准。最有名的是ANSI C和原始C++,但是编译器和链接器应用程序遵循它们自己的标准,其中有许多简化的特性。addition@Aak字体原来的ANSI C标准(1989)在1990被ISO取代,此后所有的C和C++标准都来自ISO(WG14和WG21)。。主要的附加标准是POSIX,它遵从ISO C。不过,请随意引用您认为应该应用的任何其他标准。@Aak:定义。这不是唯一可能的定义。而且
将定义类型为
的对象,而不是别名。您可以使用
typedef
使用
定义别名对不起,但是“我认为这不是有效的”只是一个意见。它绝对允许混合C和C++风格,即使是一个坏主意。C++仍然允许未命名的类,和“链接的名字”。结构{{};TyEDEFF A;B::()({})不是有效的C++,都不是C代码!有效的C代码不一定是有效的C++代码。FC++,C++ 03有一个从字符串文字到 char */COD>的隐式转换。代码> >可写字符串在GCC上警告。第二个ARG的关键性并不重要。我用多种ARG进行实验,没有效果。我用文字来保持代码的简短。TyPuvvs VS不是Type,这是关键的区别。你的理论的问题是C++链接。
 typedef struct {
     void foo();
 } B;
 void B::foo() { }
typedef struct
{
   char * characters;
   int length;
} String;