37

Is there any specific cases you cannot correctly do with std::conjunction/std::disjunction and not using the more "fundamental" (i.e. language feature instead of library feature) fold expression over &&/||?

Example:

// func is enabled if all Ts... have the same type
template<typename T, typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...> >
func(T, Ts...) {
 // TODO something to show
}

vs

// func is enabled if all Ts... have the same type
template<typename T, typename... Ts>
std::enable_if_t<(std::is_same<T, Ts> &&...)>
func(T, Ts...) {
 // TODO something to show
}

The version using a fold expression is more brief and generally more readable (although opinions might differ on that). So I don't see why it was added to the library together with fold expressions.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • "In-house" implementations of `conjunction` and `disjunction` are still useful in projects where C++17 fold expressions are not available (for whatever reason). – Emile Cormier Jan 17 '21 at 21:19

1 Answers1

44

std::conjunction short-circuits ::value instantiation, while the fold expression doesn't. This means that, given:

template <typename T> 
struct valid_except_void : std::false_type { };

template <> 
struct valid_except_void<void> { };

The following will compile:

template <typename... Ts>
constexpr auto test = std::conjunction_v<valid_except_void<Ts>...>;

constexpr auto inst = test<int, void>;

But the following won't:

template <typename... Ts>
constexpr auto test = (valid_except_void<Ts>::value && ...);

constexpr auto inst = test<int, void>;

live example on godbolt.org


From cppreference:

Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 8
    Minior nitpick: [Fold expressions do short circuit](https://stackoverflow.com/questions/50111274/are-fold-expressions-subject-to-short-circuiting/50115876). It's they just need to instantiate all members of the pack before they check the values while `std::conjunction` only instantiate members as needed. still +1 – NathanOliver Mar 14 '19 at 12:55
  • What causes a template to not be instantiated? Is the function special in that way or do all parameter packs that are not used directly kept uninstantiated? – user975989 Mar 14 '19 at 23:02
  • 3
    @user975989: it's not the template, it's the `::value` access that is short-circuited. This is done by the implementation of `conjunction` – Vittorio Romeo Mar 15 '19 at 16:15
  • 1
    Is there any point to using `conjunction/disjunction` with standard type traits that **always** have `::value` defined? Or am I wrong about this assumption? – r3mus n0x Aug 14 '19 at 13:41
  • @r3musn0x to avoid unnecessary instantiation – L. F. Mar 07 '20 at 08:56
  • Conjunction is short-circuiting: if there is a template type argument Bi with `bool(Bi::value) == false`, then instantiating `conjunction::value` does not require the instantiation of `Bj::value` for j > i. – KaiserKatze Jan 16 '21 at 03:18