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

Является ли `extern 'C" `частью типа функции?

Я не вижу комментариев в стандарте, кроме связанных с привязкой вещей.

Хотя стандарт ничего не говорит о вызове конвенции, соглашения о вызовах могут быть разными между C и С++ в реальном мире, поэтому я ожидал, что типы C-функции и С++-функции различны. Но, похоже, это не так, особенно в GCC.

#include <type_traits>

extern "C" {
  int c_func(int);
}

int cpp_func(int);

static_assert(!std::is_same<decltype(c_func), decltype(cpp_func)>::value,
              "It should not be the same type");

static_assert терпит неудачу, поскольку GCC считает, что эти функции имеют один и тот же тип.

  • Является ли extern "C" частью типа функции?
  • Как проверить, использует ли функция C-соглашение или соглашение на С++?
4b9b3361

Ответ 1

Стандарт дает понять, что языковая связь действительно является свойством самого типа функции:

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

Если это было недостаточно ясно, есть примечание (выделение мое), которое делает намеченное значение однозначным:

[Примечание. Поскольку привязка языка является частью типа функции,, если косвенным путем через указатель на C функция, функция, к которой относится полученное значение lvalue, считается функцией C. - конечная нота]

Кроме того,

Два типа функций с различным языком  Связи - это разные типы, даже если они идентичны друг другу.

Итак, ответ на ваш первый вопрос:

  • Да, extern "C" является частью типа функции.

Однако большинство компиляторов не могут различать типы функций с языковыми связями C и С++. Это, например, давняя ошибка в GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316, см. Список дубликатов). Я не читал всю нить внимательно, но, похоже, что много существующего кода сломается, если GCC начнет применять правило, что они действительно разные. Вероятно, это также связано с тем, что другие компиляторы также не соответствуют стандарту.

Учитывая, что ответ на ваш второй вопрос будет выглядеть следующим образом:

  • Вероятно, нет портативного способа выполнить эту проверку во время компиляции. Конечно, после перевода вы всегда можете зайти и посмотреть на объектный файл и посмотреть, искажено ли имя или нет.

Но теоретически ваше статическое утверждение должно работать так, как вы думаете. Это не так на практике.

Добавление:

Если мое понимание стандарта верное, то, например, следующий шаблон функции

template <typename R, typename... A>
void f(R(*)(A...));

не может быть создан для создания функции, которая принимала бы указатель на функцию с привязкой языка C в качестве аргумента, так как тип R(*)(A...) является "указателем на функцию с языком ссылок на С++, принимающим аргументы типов A... и возврата R".

Если компиляторы действительно так работали, легко понять, как вы могли бы определить, имеет ли функция связь языка C или С++.

Но в этом примере также должно быть ясно, как плохо существующий код сломается, если компиляторы действительно работали таким образом.