Support detection of whether a predicate functor is transparent.
More...
Namespaces |
namespace | bslmf |
Detailed Description
- Outline
-
-
- Purpose:
- Support detection of whether a predicate functor is transparent.
-
- Classes:
-
- See also:
- Component bslstl_map, Component bslstl_set
-
- Description:
- This component provides a metafunction,
bslmf::IsTransparentPredicate
, that can be used to detect whether a comparator is transparent (supports heterogeneous comparisons). If the comparator has a nested type named is_transparent
, the template inherits from true_type
, otherwise it inherits from false_type
.
-
- Usage:
- This section illustrates intended use of this component.
-
- Example 1: Specifying Behavior Of Comparator:
- In this example, we demonstrate the use of
IsTransparentPredicate
to determine different comparator's behavior. Our goal is to create an overload of an associative container's method that participates in overload resolution only if the comparator is transparent, otherwise we fall back on a default behavior.
- First, we define simple container,
Vector
, that is used as a foundation for our associative container: template <class TYPE>
class Vector {
const TYPE * d_begin_p;
const TYPE * d_end_p;
public:
Vector(const TYPE *first, const TYPE *last)
: d_begin_p(first)
, d_end_p(last)
{
}
const TYPE *begin() const
{
return d_begin_p;
}
const TYPE *end() const
{
return d_end_p;
}
};
Then we define simple type, String
, that is used as a key. Note that we have comparison operators that allow to compare both two String
objects and String
object with character sequence. class String {
static int s_numObjectsCreated;
const char *d_data_p;
public:
static int numObjectsCreated()
{
return s_numObjectsCreated;
}
String()
: d_data_p("")
{
++s_numObjectsCreated;
}
String(const char *data)
: d_data_p(data)
{
++s_numObjectsCreated;
}
friend bool operator<(const String& lhs, const String& rhs)
{
const char *lhsData = lhs.d_data_p;
const char *rhsData = rhs.d_data_p;
while (true) {
if (*lhsData < *rhsData) {
return true;
}
else if (*lhsData > *rhsData || *lhsData == '\0') {
return false;
}
++lhsData;
++rhsData;
}
}
friend bool operator<(const String& lhs, const char *rhs)
{
const char *lhsData = lhs.d_data_p;
while (true) {
if (*lhsData < *rhs) {
return true;
}
else if (*lhsData > *rhs || *lhsData == '\0') {
return false;
}
++lhsData;
++rhs;
}
}
friend bool operator<(const char *lhs, const String& rhs)
{
const char *rhsData = rhs.d_data_p;
while (true) {
if (*lhs < *rhsData) {
return true;
}
else if (*lhs > *rhsData || *lhs == '\0') {
return false;
}
++lhs;
++rhsData;
}
}
};
int String::s_numObjectsCreated = 0;
Next we define our associative container. Note that we use IsTransparentPredicate
for a method with enable_if
in the return type. This adds our function template into the function overload resolution set if and only if the comparison function object is considered transparent, using a technique known as "SFINAE". template <class t_COMPARATOR>
class FlatStringSet {
t_COMPARATOR d_comparator;
Vector<String> d_data;
public:
typedef const String * const_iterator;
template <class INPUT_ITERATOR>
FlatStringSet(INPUT_ITERATOR first,
INPUT_ITERATOR last,
const t_COMPARATOR& comparator = t_COMPARATOR());
const_iterator begin() const;
const_iterator end() const;
const_iterator find(const String& key) const
{
for (const_iterator first = begin(); first != end(); ++first) {
if (d_comparator(key, *first)) {
return end();
}
if (!d_comparator(*first, key)) {
return first;
}
}
return end();
}
template <class t_KEY>
typename bsl::enable_if<IsTransparentPredicate<t_COMPARATOR,
t_KEY>::value,
const_iterator>::type
find(const t_KEY& key) const
{
for (const_iterator first = begin(); first != end(); ++first) {
if (d_comparator(key, *first)) {
return end();
}
if (!d_comparator(*first, key)) {
return first;
}
}
return end();
}
int size() const;
};
template <class t_COMPARATOR>
template <class INPUT_ITERATOR>
FlatStringSet<t_COMPARATOR>::FlatStringSet(INPUT_ITERATOR first,
INPUT_ITERATOR last,
const t_COMPARATOR& comparator)
: d_comparator(comparator),
d_data(first, last)
{
}
template <class t_COMPARATOR>
typename FlatStringSet<t_COMPARATOR>::const_iterator
FlatStringSet<t_COMPARATOR>::begin() const
{
return d_data.begin();
}
template <class t_COMPARATOR>
typename FlatStringSet<t_COMPARATOR>::const_iterator
FlatStringSet<t_COMPARATOR>::end() const
{
return d_data.end();
}
template <class t_COMPARATOR>
int FlatStringSet<t_COMPARATOR>::size() const
{
return static_cast<int>(end() - begin());
}
Then we define two comparators. These classes are completely identical except that one defines an is_transparent
type, and the other does not. struct TransparentComp
{
typedef void is_transparent;
template <class LHS, class RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return lhs < rhs;
}
};
struct NonTransparentComp
{
template <class LHS, class RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return lhs < rhs;
}
};
Next we create an array of String
objects and two FlatStringSet
objects, basing on this array and having different comparators, transparent and non-transparent. Note that creation of FlatStringSet
object does not increase number of created String
objects, because it just holds pointers to these objects, but does not copy them. int OBJECTS_NUMBER = String::numObjectsCreated();
assert(0 == OBJECTS_NUMBER);
String data[] = { "1", "2", "3", "5" };
enum { dataSize = sizeof data / sizeof *data };
assert(OBJECTS_NUMBER + 4 == String::numObjectsCreated());
OBJECTS_NUMBER = String::numObjectsCreated();
FlatStringSet<NonTransparentComp> nts(data, data + dataSize);
FlatStringSet< TransparentComp> ts(data, data + dataSize);
assert(4 == nts.size() );
assert(4 == ts.size() );
assert(OBJECTS_NUMBER == String::numObjectsCreated());
Then we call find
method of set, having non-transparent comparator. Explicit creation of temporary String
object predictably increases the number of created String
objects. But using character sequence as a parameter for find
method increases it too. Because comparison operator that accepts two String
objects is used instead of operator that accepts String
and char sequence, so String
constructor is called implicitly. assert(nts.begin() + 0 == nts.find(String("1")) );
assert(nts.begin() + 1 == nts.find(String("2")) );
assert(nts.begin() + 2 == nts.find(String("3")) );
assert(nts.begin() + 3 == nts.find(String("5")) );
assert(nts.end() == nts.find(String("6")) );
assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
OBJECTS_NUMBER = String::numObjectsCreated();
assert(nts.begin() + 0 == nts.find( "1" ) );
assert(nts.begin() + 1 == nts.find( "2" ) );
assert(nts.begin() + 2 == nts.find( "3" ) );
assert(nts.begin() + 3 == nts.find( "5" ) );
assert(nts.end() == nts.find( "6" ) );
assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
OBJECTS_NUMBER = String::numObjectsCreated();
Finally we call find
method of set, having transparent comparator. Explicit creation of temporary String
object still increases the number of created String
objects. But using character sequence as a parameter for find
method does not. Because comparison operator that accepts String
and char sequence is available and more appropriate then operator accepting two String
objects. So there is no need for implicit constructor invocation. assert( ts.begin() + 0 == ts.find(String("1")) );
assert( ts.begin() + 1 == ts.find(String("2")) );
assert( ts.begin() + 2 == ts.find(String("3")) );
assert( ts.begin() + 3 == ts.find(String("5")) );
assert( ts.end() == ts.find(String("6")) );
assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
OBJECTS_NUMBER = String::numObjectsCreated();
assert( ts.begin() + 0 == ts.find( "1" ) );
assert( ts.begin() + 1 == ts.find( "2" ) );
assert( ts.begin() + 2 == ts.find( "3" ) );
assert( ts.begin() + 3 == ts.find( "5" ) );
assert( ts.end() == ts.find( "6" ) );
assert(OBJECTS_NUMBER == String::numObjectsCreated());