Outline
Purpose
Support detection of whether a predicate functor is transparent.
Classes
- See also
- bslstl_map, 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;
}
{
return d_end_p;
}
};
T::iterator end(T &container)
Definition bslstl_iterator.h:1523
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)
{
++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;
bool operator<(const MetricId &lhs, const MetricId &rhs)
BSLS_KEYWORD_CONSTEXPR CONTAINER::value_type * data(CONTAINER &container)
Definition bslstl_iterator.h:1231
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;
}
}
}
template <class t_KEY>
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)) {
}
if (!d_comparator(*first, key)) {
return first;
}
}
}
};
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());
}
bsl::size_t size(const TYPE &array)
Return the number of elements in the specified array.
T::iterator begin(T &container)
Definition bslstl_iterator.h:1495
Definition bslmf_enableif.h:525
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());