Почему структура typedef создает сбой связи

Итак, у меня есть кусок кода, который выглядит так.

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

и

void mystruct::foobar(int x, char* y) { return; }

и

mystruct obj;
obj.foobar(17, "X");

Все это компилируется, соединяется и работает отлично. За исключением случаев, когда этого не происходит. На одном компиляторе он работает, а на другом компиляторе (Android GCC) он терпит неудачу с ошибкой ссылки: неудовлетворительная ссылка.

Если я меняю его так, он компилирует и связывает.

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

Я думаю, что я знаю, почему, но я не могу объяснить это правильно, и я не могу найти его в стандарте. Может ли кто-нибудь объяснить это мне и найти правильную ссылку?

Изменить: я думал, что всем было очевидно, что это код на С++. Я отметил его; функция в структуре недействительна C; но просто чтобы быть ясным, файл имеет расширение CPP, а компилятор рассматривает его как С++.

Изменить: ответчик заметил, что вызов является литералом и поэтому является константой, но arg не является константой. Это не является критическим фактором, потому что (a) компилятор передал его (b) связанный сбой, независимо от типов аргументов.

Изменить: Моя теория заключалась в том, что это связано с анонимными типами структуры, переданными компоновщику, так что декларация и вызов, скомпилированные отдельно, не совпадают. Кажется, это может быть неверно, и в этом случае это может быть просто тонкая ошибка компилятора.

Изменить: из любопытства, может ли кто-нибудь воспроизвести это поведение? Фактический компилятор - Android NDK, недавняя загрузка и любая версия GCC. Если у других компиляторов есть/нет этой проблемы, это может быть ответ.

Ответы

Ответ 1

Я думаю, что это ошибка компилятора. Следующий более экстремальный пример все еще в порядке:

typedef struct { void f(); } f;
void f::f() { }

Из GCC buglist. То есть вам разрешено использовать имя typedef для определения функции-члена, даже если эта функция-член имеет то же имя, что и typedef.

Ответ 2

С++ 11, 9.3/5

Если определение функции-члена лексически выходит за пределы его определения класса, имя функции-члена должно быть квалифицировано по имени его класса с помощью оператора ::.

В первом примере mystruct не является именем класса.

Ответ 3

В основном собственное имя структуры находится в первой строке перед {скобкой. Последнее слово - это другое имя, которое переименовано в typedef.

Также необходимо использовать так:

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

Ответ 4

Идиома анонимной структуры в typedef - это тот, который я использовал часто, когда я впервые узнал C. Например, следующие строки являются допустимым описанием типа в C

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

Если gcc дросселируется на этом, gcc ошибочно.

Ответ 5

Кажется, вы смешиваете C и С++. Во-первых, если вы хотите, чтобы код был С++, то вы определенно должны иметь:

void foobar(int, const char*);

потому что "X" имеет тип (const char *), а не (char *).

Во-вторых, если вы хотите, чтобы ваш код был C, вы не можете использовать функцию в структуре. Разрешены только указатели на функцию C. Итак,

typedef struct {
    void (*foo)(void);
} X;

Ваш первый код действителен в коде С++, но это, вероятно, нечто иное, чем вы действительно хотите.

typedef struct {...} A; в С++ - структура без имен с typedef. Это что-то вроде:

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

Я не думаю, что это была бы правильная конструкция С++. Удаление "A" вы получили точно:

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