19

I'd like to do this:

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

But I get an error "exception specifications are not allowed in type aliases." (clang in version 6.1 of Xcode)

Is there a workaround to create an alias with a noexcept specifier?

I'm looking for something that works as defined by the language (not an extension) for cross-platform capabilities.

Michael Gazonda
  • 2,720
  • 1
  • 17
  • 33

4 Answers4

19

The standard explicitly forbids an exception specification from appearing in a typedef or an alias declaration. But it also states that the exception specifier may appear in a function pointer type.

§15.4/2 [except.spec]

An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator. An exception-specification shall not appear in a typedef declaration or alias-declaration.

And if a pointer to function does have an exception specification, then that function pointer must always be assigned a function type that has a compatible exception specification.

§15.4/5

... A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization. ...

Using these two, you can get the noexcept specification into the function pointer type in a roundabout fashion.

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

Now, you cannot assign a function without a noexcept(true) specification to a function pointer of type function_type. clang will fail to compile the code with the error

error: target exception specification is not superset of source

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 3
    Any idea what the rationale for this restriction is? – kec Dec 15 '14 at 18:29
  • I don't think §15.4/4 deals with assignment or initialization. You probably want the second paragraph of §15.4/5. – T.C. Dec 15 '14 at 18:56
  • @kec My guess is to avoid having the exception specification as part of the function type. – Praetorian Dec 15 '14 at 19:48
  • 1
    @T.C. Thank you, /5 that is indeed more applicable. – Praetorian Dec 15 '14 at 19:49
  • 2
    This workaround looks more like a Clang bug than intended behavior. – Sebastian Redl Dec 15 '14 at 19:50
  • 1
    @SebastianRedl Why do you say that? The quoted sections seem pretty clear that clang's behavior is correct, and gcc seems to erroneously ignore the exception specification when dealing with pointers to functions. – Praetorian Dec 15 '14 at 19:52
  • 3
    @Praetorian Well, if exception specification is not part of the type, then `decltype` (which returns the entity's type) shouldn't return it... – T.C. Dec 15 '14 at 21:37
  • @T.C. Good point, I have no idea what's happening then ... maybe 15.4/16.3 applies? Or it is indeed a clang bug. Thoughts? – Praetorian Dec 15 '14 at 22:26
  • 1
    If an exception specification isn't a part of the type, then why is it described how to deal with the situation where a function pointer can point to a function with an exception specifier? What I mean is that, how else could the exception specifier be a part of the function pointer if it's not a part of its type? – Michael Gazonda Dec 16 '14 at 01:14
  • @Michael 15.4/13 states clearly that the exception specification is not part of the function type. But there's all the elaborate stuff about how it can be included in a function pointer type, and how assignments to that function pointer must have compatible exception specifications. What's unclear is whether clang's behavior where it extends that to an alias declaration is as intended by the standard or not. – Praetorian Dec 16 '14 at 04:10
  • 3
    C++17 made exception specification part of the type – JVApen Dec 07 '18 at 18:38
9

An alternative to Praetorian's answer, which doesn't involve declaring a variable:

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

The unused_function is declared, but not defined.

Michael Gazonda
  • 2,720
  • 1
  • 17
  • 33
7

As a corollary to this answer, the pattern you seek is, simply:

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

I.e.

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

This does not work with extern linkage, including extern "C", i.e.:

// 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);
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
3

As a further simplification of this answer, we can use the type trait std::add_pointer as follows:

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

From the cppreference documentation (emphasis mine):

If T is a reference type, then provides the member typedef type which is a pointer to the referred type.

Otherwise, if T names an object type, a function type that is not cv- or ref-qualified, or a (possibly cv-qualified) void type, provides the member typedef type which is the type T*.

Otherwise (if T is a cv- or ref-qualified function type), provides the member typedef type which is the type T.

In other words, std::add_pointer is implemented in such a way that it's compatible with Function signature-like expressions as C++ template arguments.

The limitation on extern "C" functions still applies, so you would need to use the workaround in the answer I linked.

Community
  • 1
  • 1