Boost C++ Libraries: Ticket #10990: cpp_rational is not nothrow move constructible https://svn.boost.org/trac10/ticket/10990 <p> boost::multiprecision::cpp_rational's move constructor is not noexcept. This does not allow programmers to make objects containing a cpp_rational data member nothrow move constructible. Maybe that's not a severe bug, but an inconvenience at least. </p> <p> #include &lt;iostream&gt; #include &lt;iomanip&gt; #include &lt;type_traits&gt; #include &lt;boost/multiprecision/cpp_int.hpp&gt; int main() { </p> <blockquote> <p> using namespace std; using namespace boost::multiprecision; cout &lt;&lt; boolalpha; cout &lt;&lt; is_nothrow_move_constructible&lt;cpp_int&gt;::value &lt;&lt; endl; cout &lt;&lt; is_nothrow_move_assignable&lt;cpp_int&gt;::value &lt;&lt; endl; cout &lt;&lt; is_nothrow_move_constructible&lt;cpp_rational&gt;::value &lt;&lt; endl; cout &lt;&lt; is_nothrow_move_assignable&lt;cpp_rational&gt;::value &lt;&lt; endl; </p> </blockquote> <p> } </p> <p> Outputs: true true false true </p> <p> I believe there is a problem in rational_adaptor (see &lt;rational_adaptor.hpp&gt;, line 60), since its move constructor is defined as follows: </p> <p> rational_adaptor(rational_adaptor&amp;&amp; o) : m_value(o.m_value) {} </p> <p> Here, o.m_value is treated like an lvalue despite it can be moved from. Thus new object's m_value is copy-constructed, not move-constructed. That does not allow cpp_rational's move constructor to be noexcept (it is noexcept only if its backend class has a noexcept move ctor), also it may lead to some minor loss of efficiency. </p> <p> This problem can be fixed by rewriting mentioned rational_adaptor's move constructor this way: </p> <blockquote> <p> rational_adaptor(rational_adaptor&amp;&amp; o) </p> <blockquote> <p> BOOST_NOEXCEPT_IF(noexcept(rational_type(std::declval&lt;rational_type&gt;()))) </p> </blockquote> <p> : m_value(static_cast&lt;rational_type&amp;&amp;&gt;(o.m_value)) {} </p> </blockquote> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/10990 Trac 1.4.3 John Maddock Sat, 07 Feb 2015 17:57:32 GMT <link>https://svn.boost.org/trac10/ticket/10990#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10990#comment:1</guid> <description> <p> Thanks for the catch, I need to do some more testing of the other types in the library but for now here's the patch for this this specific case: <a class="ext-link" href="https://github.com/boostorg/multiprecision/commit/33630bc407b044a98cd8c3b0dce93ca22a01a002"><span class="icon">​</span>https://github.com/boostorg/multiprecision/commit/33630bc407b044a98cd8c3b0dce93ca22a01a002</a> </p> </description> <category>Ticket</category> </item> <item> <author>Taras Morozovsky <taras.morozovsky@…></author> <pubDate>Sun, 08 Feb 2015 00:41:14 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10990#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10990#comment:2</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/10990#comment:1" title="Comment 1">johnmaddock</a>: </p> <blockquote class="citation"> <p> Thanks for the catch, I need to do some more testing of the other types in the library but for now here's the patch for this this specific case </p> </blockquote> <p> Thank you for your reply! </p> <p> Please, keep in mind that arguments received by rvalue reference parameters are themselves lvalues. And so, in order to enable moving from them, they must be cast to rvalues once again. Without it, copy operations will be invoked instead, which may be dangerous since they may throw exceptions (std::bad_alloc in particular) when we promise that current operation will never throw. </p> <p> That's why ...<strong>: m_value(o.m_value) {}</strong> at line 60 and ...<strong>: m_value(o) {}</strong> at line 61 should be turned into ...<strong>: m_value(static_cast&lt;rational_type&amp;&amp;&gt;(o.m_value)) {}</strong> and ...<strong>: m_value(static_cast&lt;<a class="missing wiki">IntBackend</a>&amp;&amp;&gt;(o)) {}</strong> respectively. </p> <p> Considering line 61, I believe this <strong>std::declval&lt;integer_type&gt;()</strong> must be <strong>std::declval&lt;<a class="missing wiki">IntBackend</a>&gt;()</strong> instead, since a rational_type (m_value) is being constructed from an rvalue reference to <a class="missing wiki">IntBackend</a> (o), not from a number&lt;<a class="missing wiki">IntBackend</a>&gt; (which integer_type is). </p> <p> And one more thing I noticed: the condition inside BOOST_NOEXCEPT_IF at line 62. Now, it contains an expression <strong>std::declval&lt;rational_type&gt;() = std::declval&lt;rational_type&gt;()</strong> inside the noexcept operator. I think, it should be <strong>std::declval&lt;rational_type&amp;&gt;() = std::declval&lt;rational_type&gt;()</strong> instead (with an lvalue reference on the left-hand side), because m_value is itself an lvalue at line 64 where it is being assigned to. </p> <p> Hope this will help someway! </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Sun, 08 Feb 2015 18:22:17 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/10990#comment:3 https://svn.boost.org/trac10/ticket/10990#comment:3 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> Thanks, you're right of course, many more fixes in <a class="ext-link" href="https://github.com/boostorg/multiprecision/commit/9927d49cb9d87c022c3ac49b3c8d84ce55ab7dc4"><span class="icon">​</span>https://github.com/boostorg/multiprecision/commit/9927d49cb9d87c022c3ac49b3c8d84ce55ab7dc4</a>. </p> <p> Aside: I find it deeply frustrating that noexcept allows you to shoot yourself in the foot like this - the very least the compiler could do is issue a warning in the case that a noexcept function calls a non-noexcept one, otherwise I fear this is painfully easy to break! </p> <p> Please reopen if you spot any other issues. </p> Ticket