Document number: | P0013R0 |

Date: | 2015-07-05 |

Project: | Programming Language C++, Library Evolution Working Group |

Reply-to: | Jonathan Wakely <cxx@kayari.org> |

- Introduction
- Motivation and Scope
- Impact On The Standard
- Design Decisions
- Technical Specification
- Sample Implementation
- Acknowledgements
- References

I propose three new type traits for performing logical operations with other
traits, `and_`

, `or_`

, and `not_`

,
corresponding to the operators
`&&`

, `||`

, and `!`

respectively.
These utilities are used extensively in libstdc++ and are valuable additions
to any metaprogramming toolkit.

// logical conjunction:template<class... B> struct and_;// logical disjunction:template<class... B> struct or_;// logical negation:template<class B> struct not_;

The proposed traits apply a logical operator to the result of one or more
type traits, for example the specialization
```
and_<is_copy_constructible<T>, is_copy_assignable<T>>
```

has a BaseCharacteristic of `true_type`

only if `T`

is copy constructible and copy assignable, or in other words it has a
BaseCharacteristic equivalent to
`bool_constant<is_copy_constructible_v<T> && is_copy_assignable_v<T>>`

.

The traits are especially useful when dealing with variadic templates
where you want to apply the same predicate or transformation to every
element in a parameter pack, for example
```
or_<is_nothrow_default_constructible<T>...>
```

can be used to determine whether at least one of the types in the pack
`T`

has a non-throwing default constructor.

Practicing metaprogrammers often consider making more of our unary type traits
and/or type relationship traits variadic; a request that arises
seemingly very often is having something like 'are_same' which would be a
variadic `is_same`

.
**The proposed conjunction trait allows turning any trait into
a variadic trait,
without adding a plethora of variadic counterparts for individual
traits that we already have.**
The disjunction and negation complete the set.

Quoth Ville: "`__and_`

, _`_or_`

and _`_not_`

as they are already available in libstdc++ are an absolute godsend, not
just for a library
writer, but for any user who needs to do metaprogramming with parameter packs.
Boost.MPL has shipped similar facilities for a very long time, and
it's high time we standardize these simple utilities."

To demonstrate how to use `and_`

with `is_same`

to
make an 'all_same' trait, constraining a variadic function template so that
all arguments have the same type can be done using
`and_<is_same<T, Ts>...>`

, for example:

```
```*// Accepts one or more arguments of the same type.*
template<typename T, typename... Ts>
enable_if_t< and_v<is_same<T, Ts>...> >
func(T, Ts...)
{ }

For the sake of clarity this function doesn't do perfect forwarding,
but if the parameters were forwarding references the constraint would only
be slightly more complicated:
`and_v<is_same<decay_t<T>, decay_t<Ts>>...>`

.

Constraining all elements of a parameter pack to a specific type can be done similarly:

```
```*// Accepts zero or more arguments of type int.*
template<typename... Ts>
enable_if_t< and_v<is_same<int, Ts>...> >
func(Ts...)
{ }

Of course the three traits can be combined to form arbitrary predicates,
the specialization
`and_<or_<foo, bar>, not_<baz>>`

corresponds to
`(foo::value || bar::value) && !baz::value`

.

This is a pure addition with no dependency on anything that isn't already in
the 2014 standard. I propose it for inclusion in the Library Fundamentals TS
rather than the International Standard. If the changes are applied to the
C++17 working paper instead then `not_`

could be changed to use
`bool_constant`

.

In this proposal the class templates `and_`

and `or_`

derive from their arguments. An alternative design would be to force a
*BaseCharacteristic* of
`true_type`

or `false_type`

, i.e. instead of the
proposed design:

```
template<class B1> struct and_<B1> : B1 { };
```

we could specify it as:
```
template<class B1> struct and_<B1> : bool_constant<B1::value> { };
```

I believe the former is more flexible and preserves more information.

We could require the template arguments themselves to have a
*BaseCharacteristic* of `true_type`

or
`false_type`

but I think that would be an unnecessary restriction,
they only really require `B::value`

to be convertible to bool.
As proposed the traits will work with user-defined traits that have a nested
`value`

member of an enumeration type and other forms of trait
that don't derive from a specialization of `integral_constant`

.

The variable templates `and_v`

, `or_v`

, and
`not_v`

do not exist in libstdc++, largely because these traits
predate G++'s support for variable templates by several years. However the
examples above demonstrate their usefulness and adding them is consistent
with the other variable templates proposed by
N3854.

Many uses of these traits with parameter packs can be replaced by
fold expressions, for example
`and_v<T...>`

can be replaced by
`(true && ... && T::value)`

,
however the fold expression will instantiate `T::value`

for every
element of the pack,
whereas the the proposed behaviour of `and_`

and `or_`

only instantiates as many elements of the pack as necessary to determine the
answer, i.e. they perform short-circuiting with regard to instantiations.
This short-circuiting makes
```
and_<is_copy_constructible<T>, is_copy_assignable<T>>
```

potentially cheaper to instantiate than the logically equivalent
`bool_constant<is_copy_constructible_v<T> && is_copy_assignable_v<T>>`

.

Efficiency aside, the short-circuiting property allows these traits to be
used in contexts that would be more difficult otherwise. If we have a
trait `is_foo`

such that `is_foo<T>::value`

is ill-formed unless `T`

is a class type, the expression
`is_class_v<T> && is_foo<T>::value`

would be unsafe and must be replaced by something using a extra level of
indirection to ensure `is_foo::value`

is only instantiated when
valid, such as
`conditional_t<is_class_v<T>, is_foo<T>, false_type>::value`

.
This is almost precisely what
`and_v<is_class<T>, is_foo<T>>`

expands to, but `and_v`

expresses the intention more clearly.

The traits are given the obvious names, adjusted to avoid clashing with the
keywords of the same names.
Another option would have been `static_and`

but the context and
angle brackets should be enough to make it obvious these are templates that
are evaluated at compile-time, and the shorter names are easier to read.

```
For the purposes of SG10, I recommend a feature-testing macro named
````__cpp_lib_experimental_logical_traits`

.

Add to the synopsis in [meta.type.synop]:

// [meta.logical], logic operator traits:template<class... B> struct and_; template<class... B> constexpr bool and_v = and_<B...>::value; template<class... B> struct or_; template<class... B> constexpr bool or_v = or_<B...>::value; template<class B> struct not_; template<class B> constexpr bool not_v = not_<B>::value;

Add a new subclause in [meta]:

This subclause describes type traits for applying logical operators to other type traits.

template<class... B> struct and_ :see below{ }; template<class... B> constexpr bool and_v = and_<B...>::value;

The class template `and_`

forms the logical conjunction of its
template type arguments.
Every template type argument shall be usable as a base class and shall have
a member `value`

which is convertible to `bool`

,
is not hidden, and is unambiguously available in the type.

The BaseCharacteristic of a specialization `and_<B1, ..., BN>`

is the first type `Bi`

in the list
`true_type, B1, ..., BN`

for which `Bi::value == false`

, or if every
`Bi::value != false`

the BaseCharacteristic is `BN`

.
[*Note:*
This means a specialization of `and_`

does not necessarily
have a BaseCharacteristic of either `true_type`

or
`false_type`

.
— *end note*]

For a specialization
`and_<B1, ..., BN>`

if there is a template type argument `Bi`

with
`Bi::value == false`

then instantiating
`and_<B1, ..., BN>::value`

does not require the instantiation of `Bj::value`

for `j > i`

.
[*Note:*
This is analogous to the short-circuiting behavior of `&&`

.
— *end note*]

template<class... B> struct or_ :see below{ }; template<class... B> constexpr bool or_v = or_<B...>::value;

The class template `or_`

forms the logical disjunction of its
template type arguments.
Every template type argument shall be usable as a base class and shall have
a member `value`

which is convertible to `bool`

,
is not hidden, and is unambiguously available in the type.

The BaseCharacteristic of a specialization `or_<B1, ..., BN>`

is the first type `Bi`

in the list
`false_type, B1, ..., BN`

for which `Bi::value != false`

, or if every
`Bi::value == false`

the BaseCharacteristic is `BN`

.
[*Note:*
This means a specialization of `or_`

does not necessarily
have a BaseCharacteristic of either `true_type`

or
`false_type`

.
— *end note*]

For a specialization
`or_<B1, ..., BN>`

if there is a template type argument `Bi`

with
`Bi::value != false`

then instantiating
`or_<B1, ..., BN>::value`

does not require the instantiation of `Bj::value`

for `j > i`

.
[*Note:*
This is analogous to the short-circuiting behavior of `||`

.
— *end note*]

template<class B> struct not_ : integral_constant<bool, !B::value> { }; template<class B> constexpr bool not_v = not_<B>::value;

The class template `not_`

forms the logical negation of its
template type argument. The type `not_<B>`

is a
UnaryTypeTrait with a BaseCharacteristic of
`integral_constant<bool, !B::value>`

.

Example implementations of `and_`

and `or_`

based on
Daniel Krügler's code in libstdc++ are shown here:

```
template<class...> struct and_;
```*// not defined*
template<> struct and_<> : true_type { };
template<class B1> struct and_<B1> : B1 { };
template<class B1, class B2>
struct and_<B1, B2>
: conditional_t<B1::value, B2, B1>
{ };
template<class B1, class B2, class B3, class... Bn>
struct and_<B1, B2, B3, Bn...>
: conditional_t<B1::value, and_<B2, B3, Bn...>, B1>
{ };
template<class...> struct or_; *// not defined*
template<> struct or_<> : false_type { };
template<class B1> struct or_<B1> : B1 { };
template<class B1, class B2>
struct or_<B1, B2>
: conditional_t<B1::value, B1, B2>
{ };
template<class B1, class B2, class B3, class... Bn>
struct or_<B1, B2, B3, Bn...>
: conditional_t<B1::value, B1, or_<B2, B3, Bn...>>
{ };

Thanks to Daniel Krügler for the original implementation in libstdc++ which inspired this proposal, and to Ville Voutilainen for nudging me to write it up and for providing part of the motivation section.

N3854, Variable Templates For Type Traits, by Stephan T. Lavavej.