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

Macros

#define bslmf_EnableIf   bslmf::EnableIf
 This alias is defined for backward compatibility.
 

Detailed Description

Outline

Purpose

Provide a utility to set up SFINAE conditions in type deduction.

Classes

Description

This component defines two meta-functions, bsl::enable_if and bslmf::EnableIf, both of which may be used to conditionally remove (potential) template instantiations as candidates for overload resolution by causing a deduced template instantiation to fail in a way compatible with the C++ SFINAE rules.

bsl::enable_if meets the requirements of the enable_if template defined in the C++11 standard [meta.trans.ptr], while bslmf::EnableIf was devised before enable_if was standardized.

The two meta-functions provide identical functionality. Both meta-functions provide a typedef type that is an alias to a (template parameter) type if a (template parameter) condition is true; otherwise, type is not provided.

Note that bsl::enable_if should be preferred over bslmf::EnableIf, and in general, should be used by new components.

Visual Studio Workaround

Because of a Visual Studio bug, described here: http://connect.microsoft.com/VisualStudio/feedback/details/332179/ the Microsoft Visual Studio compiler may not correctly associate a function declaration that uses bsl::enable_if with that function's definition, if the definition is not inline to the declaration. This bug affects at least Visual Studio 2008 and 2010. The workaround is to implement functions using bsl::enable_if inline with their declaration.

Usage

The following snippets of code illustrate basic use of the bsl::enable_if meta-function. We will demonstrate how to use this utility to control overload sets with three increasingly complex examples.

Example 1: Implementing a Simple Function with bsl::enable_if

Suppose that we want to implement a simple swap function template to exchange two arbitrary values, as if defined below:

template<class t_TYPE>
void DummySwap(t_TYPE& a, t_TYPE& b)
// Exchange the values of the specified objects, 'a' and 'b'.
{
t_TYPE temp(a);
a = b;
b = temp;
}

However, we want to take advantage of member-swap methods supplied by user- defined types, so we define a trait that can be customized by a class implementer to indicate that their class supports an optimized member-swap method:

template<class t_TYPE>
struct HasMemberSwap : bsl::false_type {
// This traits class indicates whether the (template parameter)
// 't_TYPE' has a public 'swap' method to exchange values.
};
Definition bslmf_integralconstant.h:244

Now, we implement a generic swap function template that will invoke the member swap operation for any type that specialized our trait. The use of bsl::enable_if to declare the result type causes an attempt to deduce the type t_TYPE to fail unless the specified condition is true, and this falls under the "Substitution Failure Is Not An Error" (SFINAE) clause of the C++ standard, so the compiler will look for a more suitable overload rather than fail with an error. Note that we provide two overloaded declarations that appear to differ only in their return type, which would normally raise an ambiguity error. This works, and is in fact required, in this case as the "enable-if" conditions are mutually exclusive, so that only one overload will ever be present in an overload set. Also note that the type typedef of bsl::enable_if is an alias to void when the (template parameter) type is unspecified and the (template parameter) condition value is true.

template<class t_TYPE>
swap(t_TYPE& a, t_TYPE& b)
{
a.swap(b);
}
template<class t_TYPE>
swap(t_TYPE& a, t_TYPE& b)
{
t_TYPE temp(a);
a = b;
b = temp;
}
Definition bslmf_enableif.h:525

Next, we define a simple container template, that supports an optimized swap operation by merely swapping the internal pointer to the array of elements rather than exchanging each element:

template<class t_TYPE>
class MyContainer {
// This is a simple container implementation for demonstration purposes
// that is modeled after 'std::vector'.
// DATA
t_TYPE *d_storage;
size_t d_length;
// Copy operations are declared private and not defined.
// NOT IMPLEMENTED
MyContainer(const MyContainer&);
MyContainer& operator=(const MyContainer&);
public:
MyContainer(const t_TYPE& value, int n);
// Create a 'MyContainer' object having the specified 'n' copies of
// the specified 'value'. The behavior is undefined unless
// '0 <= n'.
~MyContainer();
// Destroy this container and all of its elements, reclaiming any
// allocated memory.
// MANIPULATORS
void swap(MyContainer &other);
// Exchange the contents of 'this' container with those of the
// specified 'other'. No memory will be allocated, and no
// exceptions are thrown.
// ACCESSORS
const t_TYPE& front() const;
// Return a reference providing non-modifiable access to the first
// element in this container. The behavior is undefined if this
// container is empty.
size_t size() const;
// Return the number of elements held by this container.
};

Then, we specialize our HasMemberSwap trait for this new container type.

template<class t_TYPE>
struct HasMemberSwap<MyContainer<t_TYPE> > : bsl::true_type {
};

Next, we implement the methods of this class:

// CREATORS
template<class t_TYPE>
MyContainer<t_TYPE>::MyContainer(const t_TYPE& value, int n)
: d_storage(new t_TYPE[n])
, d_length(n)
{
for (int i = 0; i != n; ++i) {
d_storage[i] = value;
}
}
template<class t_TYPE>
MyContainer<t_TYPE>::~MyContainer()
{
delete[] d_storage;
}
// MANIPULATORS
template<class t_TYPE>
void MyContainer<t_TYPE>::swap(MyContainer& other)
{
::swap(d_storage, other.d_storage);
::swap(d_length, other.d_length);
}
// ACCESSORS
template<class t_TYPE>
const t_TYPE& MyContainer<t_TYPE>::front() const
{
return d_storage[0];
}
template<class t_TYPE>
size_t MyContainer<t_TYPE>::size() const
{
return d_length;
}

Finally, we can test that the member-swap method is called by the generic swap function. Note that the following code will not compile unless the member-function swap is used, as the copy constructor and assignment operator for the MyContainer class template are declared as private.

void TestSwap()
{
MyContainer<int> x(3, 14);
MyContainer<int> y(2, 78);
assert(14 == x.size());
assert( 3 == x.front());
assert(78 == y.size());
assert( 2 == y.front());
swap(x, y);
assert(78 == x.size());
assert( 2 == x.front());
assert(14 == y.size());
assert( 3 == y.front());
}

Example 2: Using the bsl::enable_if Result Type

For the next example, we will demonstrate the use of the second template parameter in the bsl::enable_if template, which serves as the "result" type if the test condition passes. Suppose that we want to write a generic function to allow us to cast between pointers of different types. If the types are polymorphic, we can use dynamic_cast to potentially cast between two seemingly unrelated types. However, if either type is not polymorphic then the attempt to use dynamic_cast would be a compile-time failure, and we must use static_cast instead.

#ifdef BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES

Note that if the current compiler supports alias templates C++11 feature, we can use bsl::enable_if_t alias to the "result" type of bsl::enable_if meta-function, that avoids the ::type suffix and typename prefix in the declaration of the function return type.

template<class t_TO, class t_FROM>
bsl::enable_if_t<bsl::is_polymorphic<t_FROM>::value &&
#else
template<class t_TO, class t_FROM>
t_TO>::type *
#endif
smart_cast(t_FROM *from)
// Return a pointer to the specified 'TO' type if the specified 'from'
// pointer refers to an object whose complete class publicly derives,
// directly or indirectly, from 'TO', and a null pointer otherwise.
{
return dynamic_cast<t_TO *>(from);
}
#ifdef BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES
template<class t_TO, class t_FROM>
bsl::enable_if_t<not(bsl::is_polymorphic<t_FROM>::value &&
#else
template<class t_TO, class t_FROM>
t_TO>::type *
#endif
smart_cast(t_FROM *from)
// Return the specified 'from' pointer value cast as a pointer to type
// 'TO'. The behavior is undefined unless such a conversion is valid.
{
return static_cast<t_TO *>(from);
}
Definition bslmf_ispolymorphic.h:254

Next, we define a small number of classes to demonstrate that this casting utility works correctly:

class A {
// Sample non-polymorphic type
public:
~A() {}
};
class B {
// Sample polymorphic base-type
public:
virtual ~B() {}
};
class C {
// Sample polymorphic base-type
public:
virtual ~C() {}
};
class ABC : public A, public B, public C {
// Most-derived example class using multiple bases in order to
// demonstrate cross-casting.
};

Finally, we demonstrate the correct behavior of the smart_cast utility:

void TestSmartCast()
{
ABC object;
ABC *pABC = &object;
A *pA = &object;
B *pB = &object;
C *pC = &object;
A *pA2 = smart_cast<A>(pABC);
B *pB2 = smart_cast<B>(pC);
C *pC2 = smart_cast<C>(pB);
(void) pA;
assert(&object == pA2);
assert(&object == pB2);
assert(&object == pC2);
// These lines would fail to compile
// A *pA3 = smart_cast<A>(pB);
// C *pC3 = smart_cast<C>(pA);
}

Example 3: Controlling Constructor Selection with bsl::enable_if

The final example demonstrates controlling the selection of a constructor template in a class with (potentially) many constructors. We define a simple container template based on std::vector that illustrates a problem that may occur when trying to call the constructor the user expects. For this example, assume we are trying to create a vector<int> with 42 copies of the value 13. When we pass the literal values 42 and 13 to the compiler, the "best" candidate constructor should be the template constructor that takes two arguments of the same kind, deducing that type to be int. Unfortunately, that constructor expects those values to be of an iterator type, forming a valid range. We need to avoid calling this constructor unless the deduced type really is an iterator, otherwise a compile-error will occur trying to instantiate that constructor with an incompatible argument type. We use bsl::enable_if to create a deduction context where SFINAE can kick in. Note that we cannot deduce the ::type result of a meta-function, and there is no result type (as with a regular function) to decorate, so we add an extra dummy argument using a pointer type (produced from bsl::enable_if::type) with a default null argument:

template<class t_TYPE>
class MyVector {
// This is a simple container implementation for demonstration purposes
// that is modeled after 'std::vector'.
// DATA
t_TYPE *d_storage;
size_t d_length;
// NOT IMPLEMENTED
MyVector(const MyVector&);
MyVector& operator=(const MyVector&);
public:
// CREATORS
MyVector(const t_TYPE& value, int n);
// Create a 'MyVector' object having the specified 'n' copies of
// the specified 'value'. The behavior is undefined unless
// '0 <= n'.
template<class t_FORWARD_ITERATOR>
MyVector(t_FORWARD_ITERATOR first, t_FORWARD_ITERATOR last,
typename bsl::enable_if<
// Create a 'MyVector' object having the same sequence of values as
// found in the range described by the specified iterators
// '[first, last)'. The behavior is undefined unless 'first' and
// 'last' refer to a sequence of values of the (template parameter)
// type 't_TYPE' where 'first' is at a position at or before
// 'last'. Note that this function is currently defined inline to
// work around an issue with the Microsoft Visual Studio compiler.
{
d_length = 0;
for (t_FORWARD_ITERATOR cursor = first; cursor != last; ++cursor) {
++d_length;
}
d_storage = new t_TYPE[d_length];
for (size_t i = 0; i != d_length; ++i) {
d_storage[i] = *first;
++first;
}
}
~MyVector();
// Destroy this container and all of its elements, reclaiming any
// allocated memory.
// ACCESSORS
const t_TYPE& operator[](int index) const;
// Return a reference providing non-modifiable access to the
// element held by this container at the specified 'index'. The
// behavior is undefined unless 'index < size()'.
size_t size() const;
// Return the number of elements held by this container.
};
bsl::size_t size(const TYPE &array)
Return the number of elements in the specified array.
Definition bslmf_ispointer.h:138

Note that there is no easy test for whether a type is an iterator, so we assume that any attempt to call a constructor with two arguments that are not fundamental (such as int) must be passing iterators. Now that we have defined the class template, we implement its methods:

template<class t_TYPE>
MyVector<t_TYPE>::MyVector(const t_TYPE& value, int n)
: d_storage(new t_TYPE[n])
, d_length(n)
{
for (int i = 0; i != n; ++i) {
d_storage[i] = value;
}
}
template<class t_TYPE>
MyVector<t_TYPE>::~MyVector()
{
delete[] d_storage;
}
// ACCESSORS
template<class t_TYPE>
const t_TYPE& MyVector<t_TYPE>::operator[](int index) const
{
return d_storage[index];
}
template<class t_TYPE>
size_t MyVector<t_TYPE>::size() const
{
return d_length;
}

Finally, we demonstrate that the correct constructors are called when invoked with appropriate arguments:

void TestContainerConstructor()
{
const unsigned int TEST_DATA[] = { 1, 2, 3, 4, 5 };
const MyVector<unsigned int> x(&TEST_DATA[0], &TEST_DATA[5]);
const MyVector<unsigned int> y(13, 42);
assert(5 == x.size());
for (int i = 0; i != 5; ++i) {
assert(TEST_DATA[i] == x[i]);
}
assert(42 == y.size());
for (int i = 0; i != 42; ++i) {
assert(13 == y[i]);
}
}

Macro Definition Documentation

◆ bslmf_EnableIf

#define bslmf_EnableIf   bslmf::EnableIf