{"id":103981,"date":"2020-07-14T07:00:00","date_gmt":"2020-07-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103981"},"modified":"2022-06-01T19:29:49","modified_gmt":"2022-06-02T02:29:49","slug":"20200714-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200714-00\/?p=103981","title":{"rendered":"Deconstructing function pointers in a C++ template, the noexcept complication"},"content":{"rendered":"<p>Last time, we put together a little traits class to <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200713-00\/?p=103978\"> decompose a function pointer into its components<\/a>. But one thing missing from our class is the <code>noexcept<\/code> qualifier.<\/p>\n<p>For the remainder of the discussion, I&#8217;ve removed the <code>First\u00adArg<\/code> and <code>Last\u00adArg<\/code> type aliases, since I came to the conclusion that they aren&#8217;t really needed. What&#8217;s left is this:<\/p>\n<pre>template&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraitsBase\r\n{\r\n  using RetType = R;\r\n  using ArgTypes = std::tuple&lt;Args...&gt;;\r\n  static constexpr std::size_t ArgCount = sizeof...(Args);\r\n  template&lt;std::size_t N&gt;\r\n  using NthArg = std::tuple_element_t&lt;N, ArgTypes&gt;;\r\n};\r\n\r\ntemplate&lt;typename F&gt; struct FunctionTraits;\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n};\r\n<\/pre>\n<p>But it falls apart when we give it a <code>noexcept<\/code> function pointer. (Note that <code>noexcept<\/code> did not become part of the function pointer type until C++17.)<\/p>\n<pre>void f()\r\n{\r\n  using T = int(*)() noexcept;\r\n  using R = FunctionTraits&lt;T&gt;::RetType; \/\/ error\r\n}\r\n<\/pre>\n<p>There is no match for <code>T<\/code> because none of our specializations support <code>noexcept<\/code> function pointers.<\/p>\n<p>So let&#8217;s add <code>noexcept<\/code> to our signatures. Let&#8217;s try this version, which takes advantage of the fact that <code>noexcept<\/code> takes a Boolean parameter that says whether the <code>noexcept<\/code> applies. Saying <code>noexcept<\/code> with no parameters is shorthand for <code>noexcept(true)<\/code>, and omitting <code>noexcept<\/code> is the same as <code>noexcept(false)<\/code>.<\/p>\n<pre>template&lt;typename R, typename... Args, bool Nonthrowing&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...) noexcept(Nonthrowing)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...) noexcept(Nonthrowing);\r\n  static constexpr bool IsNoexcept = Nonthrowing;\r\n};\r\n<\/pre>\n<p>The Microsoft compiler doesn&#8217;t like it:<\/p>\n<pre style=\"white-space: pre-wrap;\">\/\/ MSVC\r\nerror C2057: expecting constant expression\r\n    struct FunctionTraits&lt;R(*)(Args...) noexcept(Nonthrowing)&gt;\r\n                                                 ^^^^^^^^^^^\r\nerror C27027: 'Nonthrowing': template parameter not used or deducible\r\n<\/pre>\n<p>icc also doesn&#8217;t like it, but for a different reason: It&#8217;s perfectly happy to match the partial specialization to a non-<code>noexcept<\/code> function, but thinks it doesn&#8217;t apply to a <code>noexcept<\/code> function.<\/p>\n<pre>    \/\/ icc is okay with this\r\n    using Test1 = FunctionTraits&lt;int(*)(float) noexcept&gt;;\r\n\r\n    \/\/ but not this. \"error: incomplete type is not allowed\"\r\n    using Test2 = FunctionTraits&lt;int(*)(float) noexcept&gt;;\r\n<\/pre>\n<p>On the other hand, gcc and clang are okay with it and deduce <code>Nonthrowing<\/code> appropriately. I&#8217;m not sure who is right. (I didn&#8217;t check icc.)<\/p>\n<p>Well that&#8217;s a bummer. The parameter to <code>noexcept<\/code> is not deducible by the Microsoft compiler. We&#8217;ll just have to add a separate specialization.<\/p>\n<pre>template&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n  constexpr static bool IsNoexcept = false;\r\n};\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...) noexcept&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n  constexpr static bool IsNoexcept = true;\r\n};\r\n<\/pre>\n<p>Okay, so that takes care of the <code>noexcept<\/code> wrinkle. We&#8217;ll look at another attribute next time.<\/p>\n<p><b>Update<\/b>: Paragraph [temp.deduct.type]\/8 of the C++ specification lists the deducible contexts, and the <code>noexcept<\/code> specifier is not on the list. Therefore, MSVC is correct to reject it, and gcc and clang&#8217;s behavior are nonstandard extensions. This was tracked as Core Working Group <a href=\"https:\/\/cplusplus.github.io\/CWG\/issues\/2355.html\"> issue number CWG2355<\/a>, with a vote to revise the standard <a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2021\/p1018r9.html#CWG2355\"> passing in January 2022<\/a> and accepted on May 21, 2022. MSVC implemented the language change in February 2020.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Trying to infer the noexcept qualifier.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-103981","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Trying to infer the noexcept qualifier.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103981","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=103981"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103981\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=103981"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103981"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103981"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}