|
BDE 4.14.0 Production release
|
Macros | |
| #define | bslmf_EnableIf bslmf::EnableIf |
| This alias is defined for backward compatibility. | |
Provide a utility to set up SFINAE conditions in type deduction.
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.
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.
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.
Suppose that we want to implement a simple swap function template to exchange two arbitrary values, as if defined below:
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:
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.
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:
Then, we specialize our HasMemberSwap trait for this new container type.
Next, we implement the methods of this class:
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.
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.
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.
Next, we define a small number of classes to demonstrate that this casting utility works correctly:
Finally, we demonstrate the correct behavior of the smart_cast utility:
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:
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:
Finally, we demonstrate that the correct constructors are called when invoked with appropriate arguments:
| #define bslmf_EnableIf bslmf::EnableIf |