BDE 4.14.0 Production release
|
Macros | |
#define | BSLMF_VOIDTYPE(ARG) typename BloombergLP::bslmf::VoidType_1< ARG >::type |
#define | BSLMF_VOIDTYPE2(...) typename BloombergLP::bslmf::VoidType_2<__VA_ARGS__>::type |
#define | BSLMF_VOIDTYPES(...) typename BloombergLP::bslmf::VoidType<__VA_ARGS__>::type |
Provide a helper for implementing SFINAE-based metafunctions.
bsl::void_t
in C++03This component provides the alias template bsl::void_t
, as specified by the C++14 standard, and a metafunction, bslmf::VoidType
, to emulate the same functionality for older compilers that do not support alias templates, which are first specified by the C++11 standard. It further provides 3 macros, BSLMF_VOIDTYPE/2/S
, that provide a consistent way to use the alias template where supported and the class template otherwise.
All forms of the metafunction, however it is written, produce the result type void
. The usefulness of this do-nothing metafunction is that, when it is instantiated, all of its template arguments must be valid. By putting the template instantiation in a SFINAE context, any use of template parameters that name invalid dependent types are discarded by the compiler as non-viable. Thus, VoidType
is most commonly used to build metafunctions that test for the existence of a specific nested data type (see {Usage}).
The bslmf::VoidType
class template is intended to provide functionality identical to the C++14 metafunction std::void_t
, but without using C++11 alias templates. A use, in C++14-compliant code, of:
can be replaced, in BDE-compliant code using any version of standard C++, by:
This section documents the preprocessor macros defined in this component.
The following macros are for use only in a type-dependent context. They all expand to a type expression that uses either bsl::void_t<ARGS>
if alias templates are supported by the current compiler, and to typename BloombergLP::bslmf::VoidType<ARGS>::type
otherwise. The alias template form is preferred, as it consumes fewer resources while compiling code; the compiled code will be equivalent, whichever implementation is chosen. These macros support writing simple code that uses the preferred idiom supported by the current tool chain. Note that code targeting only C++11 or later can use bsl::void_t
directly with no loss of efficiency or generality. These macros are needed only to support older C++03 tool chains.
The three macros support one, two, or many arguments, and are otherwise identical. The reason for three macros is that the overwhelmingly common use cases need only one, rarely two, type expressions, so for legacy compilers that do not support variadic templates, we can use a simpler class template that has fewer type parameters.
BSLMF_VOIDTYPE( TYPE_EXPRESSION )
: This macro will expand into a type expression that aliases void
if the "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if the type expressions is not valid.
BSLMF_VOIDTYPE2( TYPE_EXPRESSION_1, TYPE_EXPRESSION_2 )
: This macro will expand into a type expression that aliases void
if each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if either of the type expressions is not valid.
BSLMF_VOIDTYPES( TYPE_EXPRESSIONS... )
: This macro will expand into a type expression that aliases void
if each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if any of the type expressions is not valid.
When compiling with a C++03 tool chain, the typename
keyword in the macro expansion, used to extract the nested type
from the class template, is not valid syntax unless the whole type expression is type (or value) dependent. Such use will produce an error, even if the supplied type expression is valid. E.g., BSLMF_VOIDTYPE(void)
will always be a (non-SFINAE) error in C++03, but is perfectly valid in C++11. Be sure to test your code with a C++03 build when deploying these macros.
The templates and macros in this component aid in creating SFINAE conditions in a conforming C++03 manner. However, several of the compilers still in production have significant issues in their handling of SFINAE. For example, the Solaris CC compiler (prior to the 12.4 release) is very forgiving and will accept most invalid code without creating a SFINAE failure. Idiomatic use of this component for compile-time reflection to detect named members of a class is known to work. Other uses should be carefully tested before deployment.
In this section we show intended use of this component.
In this example, we demonstrate the use of VoidType
to determine whether a given type T
has a member type T::iterator
. Our goal is to create a metafunction, HasIteratorType
, such that HasIteratorType<T>::k_VALUE
is true
if T::iterator
is a valid type and false
otherwise. This example is adapted from the paper proposing std::void_t
for the C++ Standard, N3911.
First, we define the base-case metafunction that returns false
:
Then, we create a partial specialization that uses VoidType
to probe for T::iterator
:
Now, we define a class that has an iterator
member and apply HasIteratorType
to it:
As WithIterator::iterator
is a valid type, BSLMF_VOIDTYPE(typename TYPE::iterator)
will be void
and the second HasIteratorType
template will be more specialized than the primary template, thus yielding a k_VALUE
of true
.
Finally, we try to instantiate HasIteratorType<int>
. Any use of BSLMF_VOIDTYPE(TYPE::iterator)
will result in a substitution failure. Fortunately, the Substitution Failure Is Not An Error (SFINAE) rule applies and the partial specialization is eliminated from consideration, resulting in the primary template being instantiated and yielding a k_VALUE
of false
:
This example demonstrates the use of VoidType
to probe for more than one type at once. As in the previous example, we are defining a metafunction. We'll define IsTraversable<T>::k_VALUE
to be true
if T::iterator
and T::value_type
both exist. First, we define a primary template that always yields false
:
Then, we create a partial specialization that uses BSLMF_VOIDTYPE2
with two parameters:
Now, we define a type that meets the requirements for being traversable:
Finally, the IsTraversable
metafunction yields true
for Traversable
but not for either WithIterator
, which lacks a value_type
member, nor int
, which lacks both iterator
and value_type
members:
#define BSLMF_VOIDTYPE | ( | ARG | ) | typename BloombergLP::bslmf::VoidType_1< ARG >::type |
#define BSLMF_VOIDTYPE2 | ( | ... | ) | typename BloombergLP::bslmf::VoidType_2<__VA_ARGS__>::type |
#define BSLMF_VOIDTYPES | ( | ... | ) | typename BloombergLP::bslmf::VoidType<__VA_ARGS__>::type |