Topic: N2855 and old std::MoveConstructible


Author: restor <akrzemi1@interia.pl>
Date: Tue, 9 Jun 2009 15:47:55 CST
Raw View
Hi,
If N2855 is included in the standard, and
std::NothrowMoveConstructible used, is there any use for
std::MoveConstructible any more?

Regards,
&rzej

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: restor <akrzemi1@gmail.com>
Date: Thu, 11 Jun 2009 11:48:56 CST
Raw View
> If N2855 is included in the standard, and
> std::NothrowMoveConstructible used, is there any use for
> std::MoveConstructible any more?

Someone in some other thread mentioned that r-values aren't easy to
comprehend for novices and even for the experts (or at least expert
novices and novice experts). Could someone who does understand the r-
values/move operations explain some things about them for me?

The usage of std::move, like:
  std::move(x);
means "treat my l-value like an r-value; destroy the object if you
like, I don't need that value anymore". Is that right?

concept std::MoveConstructible means "give me either move constructor
or a copy constructor". Is that right?

The most general version of std::swap (the primary template) provides
only basic exception safety guarantee, because requirements
std::MoveConstructible and std::MoveAssignableallow copy constructor
and copy assignment, and these may throw, right?

Apart from the "unique ownership" usage (where I would provide a move-
constructor but delete the copy-constructor), there would be two
reasons why I would provide both copy and move constructors/
assignments:
1. Because move-ctor can be faster
2. Because for move-ctor I can provide the no-throw guarantee.
Am I right?

Now, the following questions are trickier, because they relate to
N2855, which may either not be accepted, or significantly modified.

Wouldn't there be a need for a swap overload on "Nothrow" types? It's
semantics would be the same, but it would itself expose a "Nothrow"
guarantee:

 template<class T>
   requires MoveAssignable<T> && MoveConstructible<T>
   void swap(T& a, T& b); // basic guarantee

 template<class T>
   requires NothrowMoveAssignable<T> && NothrowMoveConstructible<T>
   noexcept
   void swap(T& a, T& b) {
     // do exactly the same as the other swap
   }

And further:

 auto concept NothrowSwappable<typename T> /* ... */;

But will it be possible to partially specialize the template with
noexcept specifier changed?
Surely, I would like the swap overload on vector to have a noexcept
specifier.

Basically, I am tempted to think that noexcept could be propagated
like const or volatile specifiers, so that when I write a function and
want it to throw nothing I just specify it with noexcept and let the
compiler do the work. However all the algorithm templates are not
prefixed with noexcept and will not be, becase they have to work for
throwing types too. So in order for noexcept feature to work, there
would have to be noexcept overloads for nearly every algoritm (much
like with const and mutable versions of begin()/end()), ore some
mechanism to deduce the no-throw guarantee from concept requirements.

Regards,
&rzej

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: SG <s.gesemann@gmail.com>
Date: Fri, 12 Jun 2009 11:04:04 CST
Raw View
On 11 Jun., 19:48, restor <akrze...@gmail.com> wrote:
>
> Someone in some other thread mentioned that r-values aren't easy to
> comprehend for novices and even for the experts (or at least expert
> novices and novice experts). Could someone who does understand the r-
> values/move operations explain some things about them for me?

Be sure to check out
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html
https://www.boostpro.com/trac/wiki/BoostCon09/RValue101

> The usage of std::move, like:
>   std::move(x);
> means "treat my l-value like an r-value; destroy the object if you
> like, I don't need that value anymore". Is that right?

Yes. std::move doesn't destroy or move anything. It really just
converts a reference to an unnamed rvalue reference (rvalue
expression). An rvalue is something you can destroy because nobody
would care. By using std::move you basically say "I don't care what
happens to this object". Move constructors take advantage of rvalues
through "stealing" of the rvalue's resources.

> concept std::MoveConstructible means "give me either move constructor
> or a copy constructor". Is that right?

MoveConstructible<T> applies to all types T that satisfy its
requirements. Specifically:
o) This is well-formed: T temp = (some expression yielding T&&);
o) This is well-formed: T temp = (some expression yielding U&&);
o) T has an accessible destructor
where U = remove_reference<T>::type.

> The most general version of std::swap (the primary template) provides
> only basic exception safety guarantee, because requirements
> std::MoveConstructible and std::MoveAssignable allow copy constructor
> and copy assignment, and these may throw, right?

I think you're right. Maybe there will be an overload for those types
that are NothrowMoveConstructible and NothrowMoveAssignable where
those concepts include a "noexcept" move-constructor respective move-
assignment operator.

> Apart from the "unique ownership" usage (where I would provide a move-
> constructor but delete the copy-constructor), there would be two
> reasons why I would provide both copy and move constructors/
> assignments:
> 1. Because move-ctor can be faster
> 2. Because for move-ctor I can provide the no-throw guarantee.
> Am I right?

Yes.

> Now, the following questions are trickier, because they relate to
> N2855, which may either not be accepted, or significantly modified.
>
> Wouldn't there be a need for a swap overload on "Nothrow" types? It's
> semantics would be the same, but it would itself expose a "Nothrow"
> guarantee:
>
>  template<class T>
>    requires MoveAssignable<T> && MoveConstructible<T>
>    void swap(T& a, T& b); // basic guarantee
>
>  template<class T>
>    requires NothrowMoveAssignable<T> && NothrowMoveConstructible<T>
>    noexcept
>    void swap(T& a, T& b) {
>      // do exactly the same as the other swap
>    }

Seems like a good idea. For this to work a NothrowXXX concept needs to
be a refinement of XXX, I think. I'm not 100% sure about how exactly a
compiler applies partial ordering to the requirements for overload
resolution, though.

> And further:
>
>  auto concept NothrowSwappable<typename T> /* ... */;
>
> But will it be possible to partially specialize the template with
> noexcept specifier changed?
> Surely, I would like the swap overload on vector to have a noexcept
> specifier.
>
> Basically, I am tempted to think that noexcept could be propagated
> like const or volatile specifiers, so that when I write a function and
> want it to throw nothing I just specify it with noexcept and let the
> compiler do the work. However all the algorithm templates are not
> prefixed with noexcept and will not be, becase they have to work for
> throwing types too. So in order for noexcept feature to work, there
> would have to be noexcept overloads for nearly every algoritm (much
> like with const and mutable versions of begin()/end()), ore some
> mechanism to deduce the no-throw guarantee from concept requirements.

Yes. It might get tedious. It already bugs me that you can't write
std::memfun in a truly generic way but you have to consider four cases
for different const/volatile versions of member functions. IMHO, we
ought to be able to handle things like noexcept, const, volatile
without writing different versions for every special case. One
approach would be to

   o) extend the template system with a new kind of parameter
      for cv qualifiers
   o) make noexcept parameterisable

--------8<--------[ BEGIN hypothetical C++ ]--------8<--------

   template< bool NoExcept, typename Ret, class Clz,
     qualifiers CV, typename ... Parms >
   class memfun_t
   {
   public:
     typedef noexcept<NoExcept> Ret (Clz::*pmf_t)(Parms...) CV;
     typedef Clz CV* objptr_t;

     memfun_t(pmf_t pmf) : pmf(pmf) {}

     noexcept<NoExcept>
     Ret operator()(objptr_t po, P... params) const
     {
       return (*po)(std::forward<P>(params)...);
     }
   private:
     pmf_t pmf;
   };

   template< bool NoExcept, typename Ret, class Clz,
     qualifiers CV, typename ... Parms >
   memfun_t<NoExcept,Ret,Clz,CV,Params...>
   memfun( noexcept<NoExcept> Ret (Clz::*pmf)(Params...) CV )
   {
     return memfun_t<NoExcept,Ret,Clz,CV,Params...>(pmf);
   }

--------8<--------[ END hypothetical C++ ]--------8<--------

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]