BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslalg_nothrowmovableutil

Functions

template<class TYPE >
bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type & bslalg::NothrowMovableUtil::unwrap (TYPE &f)
 
template<class TYPE >
bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type const & bslalg::NothrowMovableUtil::unwrap (TYPE const &f)
 
template<class TYPE >
bslmf::MovableRef< const typename bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type > bslalg::NothrowMovableUtil::unwrap (BSLMF_MOVABLEREF_DEDUCE(const TYPE) f)
 
template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap (TYPE &f)
 
template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap (const TYPE &f)
 
template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap (BSLMF_MOVABLEREF_DEDUCE(const TYPE) f)
 

Detailed Description

Outline

Purpose

Provide a wrapper that asserts a noexcept move constructor.

Classes

See also
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
bslmf::MovableRef<TP>, const TP&>::type
MoveIfNoexcept(TP& x);
Definition bslmf_movableref.h:751
Definition bslmf_conditional.h:120

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
/// Construct `CountedType` from the specified `val`.
CountedType(const TYPE& val);
/// Copy construct `*this` from the specified `original` object.
CountedType(const CountedType& original);
/// Move construct `*this` from `original`. If an exception is
/// thrown, by the constructor for `TYPE` `original` is unchanged.
CountedType(bslmf::MovableRef<CountedType> original);
/// Destroy this object.
~CountedType() { --s_count; }
// 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
bslmf::MovableRef<TP>, const TP&>::type
CountedType<TYPE>::MoveIfNoexcept(TP& x)
{
}
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1060

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 TYPE's 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;
}
Definition bdlbb_blob.h:576

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; }
};
static t_TYPE & access(t_TYPE &ref) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1032

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> >
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
NothrowMovableUtil_Traits< TYPE >::UnwrappedType type
Definition bslalg_nothrowmovableutil.h:370

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();'
}
};
static UnwrappedType< TYPE >::type & unwrap(TYPE &f)

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> >
assert(-1 == obj3.value().value()); // moved from
assert(3 == obj4.value().value()); // No need to call 'unwrap'
}

Function Documentation

◆ unwrap() [1/3]

template<class TYPE >
bslmf::MovableRef< const typename bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type > bslalg::NothrowMovableUtil::unwrap ( BSLMF_MOVABLEREF_DEDUCE(const TYPE)  f)
inline

◆ unwrap() [2/3]

template<class TYPE >
bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type & bslalg::NothrowMovableUtil::unwrap ( TYPE &  f)
inline

◆ unwrap() [3/3]

template<class TYPE >
bslalg::NothrowMovableUtil::UnwrappedType< TYPE >::type const & bslalg::NothrowMovableUtil::unwrap ( TYPE const &  f)
inline

◆ wrap() [1/3]

template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap ( BSLMF_MOVABLEREF_DEDUCE(const TYPE)  f)
inline

◆ wrap() [2/3]

template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap ( const TYPE &  f)
inline

◆ wrap() [3/3]

template<class TYPE >
bslalg::NothrowMovableUtil::WrappedType< TYPE >::type bslalg::NothrowMovableUtil::wrap ( TYPE &  f)
inline