enable if all enable if any

suggest change

Motivational example

When you have a variadic template pack in the template parameters list, like in the following code snippet:

template<typename ...Args> void func(Args &&...args) { //... };

The standard library (prior to C++17) offers no direct way to write enable_if to impose SFINAE constraints on all of the parameters in Args or any of the parameters in Args. C++17 offers std::conjunction and std::disjunction which solve this problem. For example:

/// C++17: SFINAE constraints on all of the parameters in Args.
template<typename ...Args,
         std::enable_if_t<std::conjunction_v<custom_conditions_v<Args>...>>* = nullptr>
void func(Args &&...args) { //... };

/// C++17: SFINAE constraints on any of the parameters in Args.
template<typename ...Args,
         std::enable_if_t<std::disjunction_v<custom_conditions_v<Args>...>>* = nullptr>
void func(Args &&...args) { //... };

If you do not have C++17 available, there are several solutions to achieve these. One of them is to use a base-case class and partial specializations, as demonstrated in answers of this question.

Alternatively, one may also implement by hand the behavior of std::conjunction and std::disjunction in a rather straight-forward way. In the following example I’ll demonstrate the implementations and combine them with std::enable_if to produce two alias: enable_if_all and enable_if_any, which do exactly what they are supposed to semantically. This may provide a more scalable solution.


Implementation of enable_if_all and enable_if_any


First let’s emulate std::conjunction and std::disjunction using customized seq_and and seq_or respectively:

/// Helper for prior to C++14.
template<bool B, class T, class F >
using conditional_t = typename std::conditional<B,T,F>::type;

/// Emulate C++17 std::conjunction.
template<bool...> struct seq_or: std::false_type {};
template<bool...> struct seq_and: std::true_type {};

template<bool B1, bool... Bs>
struct seq_or<B1,Bs...>: 
  conditional_t<B1,std::true_type,seq_or<Bs...>> {};

template<bool B1, bool... Bs>
struct seq_and<B1,Bs...>:
  conditional_t<B1,seq_and<Bs...>,std::false_type> {};

Then the implementation is quite straight-forward:

template<bool... Bs>
using enable_if_any = std::enable_if<seq_or<Bs...>::value>;

template<bool... Bs>
using enable_if_all = std::enable_if<seq_and<Bs...>::value>;

Eventually some helpers:

template<bool... Bs>
using enable_if_any_t = typename enable_if_any<Bs...>::type;

template<bool... Bs>
using enable_if_all_t = typename enable_if_all<Bs...>::type;

Usage


The usage is also straight-forward:

/// SFINAE constraints on all of the parameters in Args.
template<typename ...Args,
         enable_if_all_t<custom_conditions_v<Args>...>* = nullptr>
void func(Args &&...args) { //... };

/// SFINAE constraints on any of the parameters in Args.
template<typename ...Args,
         enable_if_any_t<custom_conditions_v<Args>...>* = nullptr>
void func(Args &&...args) { //... };

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you:


SFINAE:
* void_t
* enable if all enable if any

Table Of Contents
8 Arrays
11 Loops
39 Streams
51 Unions
56 Lambdas
60 SFINAE
62 RAII
67 Sorting
84 RTTI
87 Scopes
104 Profiling
107 Recursion
117 Iteration
125 Alignment
134 Semaphore
136 Debugging
139 Mutexes
142 decltype