Как создать псевдоним для указателя функции noexcept?

Я хотел бы сделать это:

using function_type = void (*)(void*)noexcept;

Но я получаю сообщение об ошибке «Спецификации исключений не допускаются в псевдонимах типов». (лязг в версии 6.1 Xcode)

Есть ли обходной путь для создания псевдонима со спецификатором noexcept?

Я ищу что-то, что работает, как определено языком (а не расширением) для кросс-платформенных возможностей.


person Michael Gazonda    schedule 15.12.2014    source источник
comment
g++ компилирует нормально. Может быть, он просто игнорирует спецификатор...   -  person DarioP    schedule 15.12.2014
comment
GCC здесь не соответствует стандарту. Кланг прав.   -  person Columbo    schedule 15.12.2014
comment
Хм, это странная ошибка gcc. Ut не принимает typedef, но принимает using.   -  person n. 1.8e9-where's-my-share m.    schedule 15.12.2014


Ответы (4)


Стандарт явно запрещает появление спецификации исключения в объявлении typedef или псевдонима. Но в нем также говорится, что спецификатор исключения может появляться в типе указателя на функцию.

§15.4/2 [кроме.spec]

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

И если указатель на функцию имеет спецификацию исключения, то этому указателю на функцию всегда должен быть назначен тип функции, имеющий совместимую спецификацию исключения.

§15.4/5

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

Используя эти два, вы можете получить спецификацию noexcept в типе указателя функции окольным путем.

void (*foo_ptr)(void *) noexcept = nullptr;
using function_type = decltype(foo_ptr);

Теперь вы не можете назначить функцию без спецификации noexcept(true) указателю функции типа function_type. clang не сможет скомпилировать код с ошибкой

ошибка: спецификация целевого исключения не является надмножеством источника

person Praetorian    schedule 15.12.2014
comment
Есть идеи, в чем причина этого ограничения? - person kec; 15.12.2014
comment
Я не думаю, что §15.4/4 касается присваивания или инициализации. Вам, вероятно, нужен второй абзац §15.4/5. - person T.C.; 15.12.2014
comment
@kec Я предполагаю, что следует избегать спецификации исключения как части типа функции. - person Praetorian; 15.12.2014
comment
@Т.С. Спасибо, /5 это действительно более применимо. - person Praetorian; 15.12.2014
comment
Этот обходной путь больше похож на ошибку Clang, чем на предполагаемое поведение. - person Sebastian Redl; 15.12.2014
comment
@SebastianRedl Почему ты так говоришь? Процитированные разделы кажутся довольно ясными, что поведение clang правильное, и gcc, похоже, ошибочно игнорирует спецификацию исключений при работе с указателями на функции. - person Praetorian; 15.12.2014
comment
@Praetorian Ну, если спецификация исключения не является частью типа, то decltype (который возвращает тип объекта) не должен его возвращать ... - person T.C.; 16.12.2014
comment
@Т.С. Хороший вопрос, я понятия не имею, что тогда происходит ... может быть, применяется 15,4/16,3? Или это действительно баг лязга. Мысли? - person Praetorian; 16.12.2014
comment
Если спецификация исключения не является частью типа, то почему описывается, как действовать в ситуации, когда указатель на функцию может указывать на функцию со спецификатором исключения? Я имею в виду, что как еще спецификатор исключения может быть частью указателя на функцию, если он не является частью его типа? - person Michael Gazonda; 16.12.2014
comment
@Michael 15.4/13 ясно указывает, что спецификация исключения не является частью типа функции. Но есть все сложные вещи о том, как его можно включить в тип указателя на функцию, и как присваивания этому указателю на функцию должны иметь совместимые спецификации исключений. Что неясно, так это то, соответствует ли поведение clang, когда он расширяет это до объявления псевдонима, стандартом или нет. - person Praetorian; 16.12.2014
comment
С++ 17 сделал спецификацию исключений частью типа - person JVApen; 07.12.2018

Альтернатива преторианскому ответу, который не требует объявления переменной:

void unused_function(void*)noexcept;
using function_type = decltype(&unused_function);

unused_function объявлен, но не определен.

person Michael Gazonda    schedule 15.12.2014
comment
И благодаря declval есть способ заявить об этом анонимно :) - person Kuba hasn't forgotten Monica; 07.12.2018

Как следствие этот ответ, шаблон, который вы ищете, просто:

using function_type = decltype(std::declval<function_type_declaration>());

I.e.

#include <utility>
//...
using function_type = decltype(std::declval<void (*)(void*)noexcept>());

Это не работает со связью extern, включая extern "C", т.е.:

// INVALID
using function_type = decltype(std::declval<extern "C" void(*)(void)>());

// WORKAROUND
extern "C" void function_type_dummy(void);
using function_type = decltype(&function_type_dummy);
person Kuba hasn't forgotten Monica    schedule 07.12.2018
comment
Это не приводит к ошибке в Clang (проверено с 11.0.0). - person mike.dld; 07.01.2021

В качестве дальнейшего упрощения этого ответа мы можем использовать признак типа std::add_pointer следующим образом:

using function_type = std::add_pointer_t<void(void*) noexcept>;

Из документации cppreference (выделено мной):

Если T является ссылочным типом, то предоставляет тип typedef члена, который является указателем на указанный тип.

В противном случае, если T называет тип объекта, тип функции, который не имеет квалификации cv или ref, или тип void (возможно, квалифицированный cv), предоставляет тип typedef члена, который является типом T *.

В противном случае (если T является типом функции с указанием cv или ref), предоставляет тип typedef члена, который является типом T.

Другими словами, std::add_pointer реализован таким образом, что он совместим с Function выражения, подобные подписи, в качестве аргументов шаблона C++.

Ограничение на функции extern "C" все еще применяется, поэтому вам нужно будет использовать обходной путь в ответе, который я связал.

person Lia Stratopoulos    schedule 19.06.2019
comment
Это не приводит к ошибке в Clang (проверено с 11.0.0). - person mike.dld; 07.01.2021