Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslalg_nothrowmovableutil
[Package bslalg]

Provide a wrapper that asserts a noexcept move constructor. More...

Namespaces

namespace  bslalg

Detailed Description

Outline
Purpose:
Provide a wrapper that asserts a noexcept move constructor.
Classes:
bslalg::NothrowMovableUtil utilities for bslalg_nothrowmovablewrapper
See also:
Component bslalg_nothrowmovablewrapper
Description:
This component provides a utility struct, bslalg::NothrowMovableUtil, for managing bslalg_nothrowmovablewrapper objects. It provides a namespace for static functions wrap and unwrap with a uniform interface such that unwrapping an object that is not wrapped or wrapping an object that is already wrapped are no-ops. This utility struct also provides type traits for determining whether a type is wrapped and for deducing the type of the wrapped and unwrapped object.
Usage:
Example:
In this example, we define a class template, CountedType<TYPE>, a wrapper around TYPE that counts the number of extant CountedType objects. We begin by defining the static count member along with the single value member:
  template <class TYPE>
  class CountedType {
      // CLASS DATA
      static int s_count;

      // DATA
      TYPE       d_value;
Because of externally-imposed requirements, the move constructor for CountedType must provide the strong guarantee; i.e., if the move constructor of TYPE throws an exception, then the moved-from CountedType object must be left unchanged. To support this requirement, we next define a private static function, MoveIfNoexcept, similar to the standard std::move_if_noexcept, that returns a movable reference if its argument is no-throw move constructible and a const lvalue reference otherwise:
      // PRIVATE CLASS FUNCTIONS
      template <class TP>
      static typename
      bsl::conditional<bsl::is_nothrow_move_constructible<TP>::value,
                       bslmf::MovableRef<TP>, const TP&>::type
      MoveIfNoexcept(TP& x);
We next finish out the class definition with a constructor, copy constructor, move constructor, destructor, and member functions to retrieve the count and value:
  public:
      // CLASS FUNCTIONS
      static int count() { return s_count; }

      // CREATORS
      CountedType(const TYPE& val);
          // Construct 'CountedType' from the specified 'val'.

      CountedType(const CountedType& original);
          // Copy construct '*this' from the specified 'original' object.

      CountedType(bslmf::MovableRef<CountedType> original);
          // Move construct '*this' from 'original'.  If an exception is
          // thrown, by the constructor for 'TYPE' 'original' is unchanged.

      ~CountedType() { --s_count; }
          // Destroy this object.

      // MANIPULATORS
      TYPE& value() { return d_value; }

      // ACCESSORS
      const TYPE& value() const { return d_value; }
  };
Next, we implement MoveIfNoexcept, which calls move on its argument, allowing it to convert back to an lvalue if the return type is an lvalue reference:
  template <class TYPE>
  template <class TP>
  inline typename
  bsl::conditional<bsl::is_nothrow_move_constructible<TP>::value,
                   bslmf::MovableRef<TP>, const TP&>::type
  CountedType<TYPE>::MoveIfNoexcept(TP& x)
  {
      return bslmf::MovableRefUtil::move(x);
  }
Next, we implement the value constructor and copy constructor, which simply copy their argument into the d_value data members and increment the count:
  template <class TYPE>
  CountedType<TYPE>::CountedType(const TYPE& val) : d_value(val)
  {
      ++s_count;
  }

  template <class TYPE>
  CountedType<TYPE>::CountedType(const CountedType& original)
      : d_value(original.d_value)
  {
      ++s_count;
  }
We're now ready implement the move constructor. Logically, we would simply move the value from original into the d_value member of *this, but an exception thrown by TYPEs move constructor would leave 'original in a (valid but) unspecified state, violating the strong guarantee. Instead, we move the value only if we know that the move will succeed; otherwise, we copy it. This behavior is facilitated by the MoveIfNoexcept function defined above:
  template <class TYPE>
  CountedType<TYPE>::CountedType(bslmf::MovableRef<CountedType> original)
      : d_value(
          MoveIfNoexcept(bslmf::MovableRefUtil::access(original).d_value))
  {
      ++s_count;
  }
Finally, we define the s_count member to complete the class implementation:
  template <class TYPE>
  int CountedType<TYPE>::s_count = 0;
To test the CountedType class template, assume a simple client type, SomeType that makes it easy to detect if it was move constructed. SomeType holds an int value that is set to -1 when it is moved from, as shown here:
  class SomeType {
      int d_value;
  public:
      SomeType(int v = 0) : d_value(v) { }                        // IMPLICIT
      SomeType(const SomeType& original) : d_value(original.d_value) { }
      SomeType(bslmf::MovableRef<SomeType> original)
          : d_value(bslmf::MovableRefUtil::access(original).d_value)
          { bslmf::MovableRefUtil::access(original).d_value = -1; }

      int value() const { return d_value; }
  };
Notice that SomeType neglected to declare its move constructor as noexcept. This might be an oversight or it could be an old class that predates both noexcept and the bsl::is_nothrow_move_constructible trait. It is even be possible that the move constructor might throw (though, of course, it doesn't in this simplified example). Regardless, the effect is that move-constructing a CountedType<SomeType> will result in the move constructor actually performing a copy:
  void main()
  {
      CountedType<SomeType> obj1(1);
      CountedType<SomeType> obj2(bslmf::MovableRefUtil::move(obj1));
      assert(1 == obj1.value().value());  // Copied, not moved from
      assert(1 == obj2.value().value());
For the purpose of this example, we can be sure that SomeThing will not throw on move, at least not in our application. In order to obtain the expected move optimization, we next wrap our 'SomeType in a bslalg::NothrowMovableWrapper:
      CountedType<bslalg::NothrowMovableWrapper<SomeType> >
          obj3(SomeType(3));
      CountedType<bslalg::NothrowMovableWrapper<SomeType> >
          obj4(bslmf::MovableRefUtil::move(obj3));
      assert(-1 == obj3.value().unwrap().value());  // moved from
      assert(3 == obj4.value().unwrap().value());
  }
Note that, in the last two lines of main, we must call unwrap in order to access the SomeType object inside of the NothrowMovableWrapper. This is one situation where it would be attractive to have an overloadable "operator dot" so that both CountedThing and NothrowMovableWrapper could be transparent proxy types. C++ does not have overloadable operator dot, but we can create a CountedType that is more intelligent about the existence of NothrowMovableWrapper and automatically unwraps values for the user's convenience.
Rather than starting from scratch, we'll build our new counted type, CountedType2 on CountedType. We start be defining a single data member of type CountedType:
  template <class TYPE>
  class CountedType2 {
      CountedType<TYPE> d_data;
Next, for convenience, we add a public data type, ValueType for the value stored within CountedType2. However, rather than defining ValueType as simply TYPE, we want to know if it is an instantiation of NothrowMovableWrapper<TP>. If it is, we want a type that represents the unwrapped TP rather than the full TYPE. For this type transformation, we turn to the type traits defined in bslalg::NothrowMovableUtil:
  public:
      // TYPES
      typedef typename
      bslalg::NothrowMovableUtil::UnwrappedType<TYPE>::type ValueType;
Note that the UnwrappedType metafunction has no affect if TYPE is not wrapped.
Next, we declare (and define) the class functions, constructors, and destructor, simply forwarding to the corresponding CountedType function, constructor, or destructor:
      // CLASS FUNCTIONS
      static int count() { return CountedType<TYPE>::count(); }

      // CREATORS
      CountedType2(const TYPE& val) : d_data(val) { }
      CountedType2(const CountedType2& original)
          : d_data(original.d_data) { }
      CountedType2(bslmf::MovableRef<CountedType2> original)
          : d_data(bslmf::MovableRefUtil::move(
                       bslmf::MovableRefUtil::access(original).d_data)) { }
Finally, we implement the value() members such that the returned values do not need to be unwrapped. As in the case of the UnwrappedType metafunction, the unwrap() function in NothrowMovableUtil handles both wrapped and unwrapped arguments, unwrapping the latter and returning an unmodified reference to the former:
      // MANIPULATORS
      ValueType& value()
      {
          return bslalg::NothrowMovableUtil::unwrap(d_data.value());
          // Alternatively: 'return d_data.value();'
      }

      // ACCESSORS
      const ValueType& value() const
      {
          return bslalg::NothrowMovableUtil::unwrap(d_data.value());
          // Alternatively: 'return d_data.value();'
      }
  };
Note the alternative code for these members: A NothrowMovableWrapper<TP> object is implicitly convertible to TP&, so if TYPE is a NothrowMovableWrapper, the simple return statement will implicitly unwrap it.
Using a similar example for CountedType2 as we used for CountedType, we see that the usage of CountedType2 with and without NothrowMovableWrapper is the same:
  void main()
  {
      CountedType2<SomeType> obj1(1);
      CountedType2<SomeType> obj2(bslmf::MovableRefUtil::move(obj1));
      assert(1 == obj1.value().value());  // Copied, not moved from
      assert(1 == obj2.value().value());

      CountedType2<bslalg::NothrowMovableWrapper<SomeType> >
          obj3(SomeType(3));
      CountedType2<bslalg::NothrowMovableWrapper<SomeType> >
          obj4(bslmf::MovableRefUtil::move(obj3));
      assert(-1 == obj3.value().value());  // moved from
      assert(3 == obj4.value().value());   // No need to call 'unwrap'
  }