Подтвердить что ты не робот

Почему С++ 11 не поддерживает объявление extern "C" для статической функции-члена?

При условии, что у меня есть библиотека C, содержащая функцию, объявленную как void g(void (*callback)()); Следующий код является элегантным, но незаконным:

struct A
{
    // error C2159: more than one storage class specified (VC++ Nov 2012 CTP)
    static extern "C" void callback()
    {}
};

g(A::callback); 

Почему С++ 11 не поддерживает это?

4b9b3361

Ответ 1

Это особенно запутанная тема. Пусть атака §7.5 "Спецификации привязки" [dcl.link].

1) Все типы функций, имена функций с внешней связью и имена переменных с внешней связью имеют языковые связи.

Обратите внимание, что свойство языковой привязки применяется к двум совершенно различным типам сущностей: типам и именам.

Функция имеет в общем виде невидимый бит информации, который идентифицирует, какой ABI он соответствует: соглашениям о вызовах C, Pascal, Fortran, все могут быть указаны для использования стека по-разному, поэтому вызов их через указатель требует знания невидимого языкового тега.

Имя переменной или функции с другого языка можно получить синтаксически через С++ или с другого языка, ссылающегося на объявление С++. Но не каждый язык может соответствовать схеме именования С++ и модели OO. Таким образом, интерфейсы в этой схеме не включают классы.

Поскольку эти вещи управляются отдельно, возможно иметь что-то с другой связью в своем типе (вызывающие соглашения) и его имя (символ линкера).

4) Спецификация спецификации привязки. Когда гнездо спецификации привязки, самый внутренний определяет язык связь. Спецификация связи не устанавливает область действия. Спецификация привязки должна выполняться только в области пространства имен (3.3). В спецификации привязки указанная языковая связь применяется к типам функций всех деклараторов функций, имен функций с внешней привязкой и именам переменных с внешней связью, объявленной в спецификации привязки. Связи языка C игнорируются при определении языковой привязки имен членов класса и типа функции функций-членов класса.

extern "C" {} влияет на все объявления функций, включая указатели и ссылки, кроме функций-членов. Поскольку функция может быть определена только в пространстве имен или как член, функции C могут быть определены только в области пространства имен.

В стандарте приведен пример:

extern "C" typedef void FUNC_c();

class C {
   // the name of the function mf1 and the member 
   // function’s type have C++ language linkage; the 
   // parameter has type pointer to C function
   void mf1(FUNC_c*);

   // the name of the function mf2 and the member
   // function’s type have C++ language linkage
   FUNC_c mf2;

   // the name of the data member q has C++ language
   // linkage and the data member’s type is pointer to
   // C function
   static FUNC_c* q;
};

Вы можете эмулировать поведение, которое хотите, используя typedef. Из другого примера в § 7.5/4,

extern "C" typedef void FUNC();

// the name f2 has C++ language linkage and the 
// function’s type has C language linkage
FUNC f2;

Объединяя эти примеры с вашими, вы можете иметь

extern "C" typedef void callback_t();

callback_t A_callback; // declare function with C++ name and C type

struct A
{
    static callback_t &callback; // not a member function
};

// in source file:

// definition matches semantics of declaration, although not syntax
void A_callback() { ... }

// define static member reference
callback_t &A::callback = A_callback;

g(A::callback); // call syntax is emulated

На практике это редко имеет значение. C и С++ используют совместимые соглашения о вызовах на большинстве платформ (см. Комментарии Джонатана Вакели на этой странице для исключений), если вы не пытаетесь передать или вернуть тип класса, отличного от POD С++. Это менее реализованная функция С++ из-за запутанной перегрузки терминов и концептуальных различий, от тонкой до академической.

Ответ 2

Во-первых, объявление функции является законным. extern "C", однако, игнорируется для членов класса, поэтому, если g ожидает extern "C" void (*)(), вы не можете передать его callback.

Что касается причин этого, я подозреваю, что изначально это было главным образом вопрос ортогональности: для класса нет смысла функция-член будет extern "C" в общем случае и ортогональность (или просто никто не рассматривал случай статических элементов) означает что это относится и к статическим функциям-членам, хотя позволяя им быть extern "C", было бы полезно. Cегодня (то есть С++ 11), было бы проблематично изменить правило, потому что он может сломать существующий код. ИМХО, это изменение быть приемлемым, потому что количество кода, которое он сломал, вероятно, очень мал, и поломка приводит к времени компиляции error — не изменение семантики времени выполнения — так легко обнаружено и исправлено. Тем не менее, насколько я знаю, никто не делал предложение изменить это, поэтому оно не изменилось.