On Wed, 29 May 2019 at 19:30, Johan Alfredsson <sduvan.gcc@xxxxxxxxx> wrote: > > OK, thanks. So I guess the eagerness of the template machinery is not > something you can work around. Perhaps the only way around this is to > avoid std::conjunction and explicitly encode a staged evaluation, for > instance: > > template <typename T> constexpr bool is_barable_v = > std::decay_t<T>::barable::value; > template <typename T, typename = void> > struct is_fooable : std::false_type {}; > > template <typename T> > struct is_fooable<T, std::void_t<typename std::decay_t<T>::baz>> : > std::bool_constant<!is_barable_v<T>> {}; > > struct Foo { > using barable = std::false_type; > using baz = bool; > }; That's certainly not the only way, and I disagree that you should avoid std::conjunction. You should avoid your is_barable_v variable template, because it evaluates too eagerly. This works fine: template <typename T> using is_fooable_2 = std::enable_if_t<std::conjunction_v<has_baz<T>, std::negation<typename std::decay_t<T>::barable>>>; Or: template <typename T> using is_barable = typename std::decay_t<T>::barable; template <typename T> using is_fooable_2 = std::enable_if_t<std::conjunction_v<has_baz<T>, std::negation<is_barable<T>>>>;