// Copyright (C) 2023 johannst #include // (1) Primary template. template struct is_valid : std::false_type {}; // (2) Partial template specialization. template struct is_valid().some_fun1()), decltype(std::declval().some_fun2())>> : std::true_type {}; struct A { void some_fun1() {} void some_fun2() {} }; struct B {}; static_assert(is_valid::value, "is true"); // * Compare template arg list with primary template, we only supplied one // arg, the second one will be defaulted as // is_valid // * Compare template arg list against available specializations, this will // try to match the pattern against the patterns defined in the // partial specializations. // * Try specialization (2) // * T -> A // * Evaluate std::void_t -> decltype's are well-formed // std::void_t<...> -> void // * Specialization (2) matches // * Pick the most specialized version -> (2) static_assert(!is_valid::value, "is false"); // * Compare template arg list with primary template, we only supplied one // arg, the second one will be defaulted as // is_valid // * Compare template arg list against available specializations, this will // try to match the pattern against the patterns defined in the // partial specializations. // * Try specialization (2) // * T -> B // * Evaluate std::void_t -> decltype's are ill-formed // * Specialization (2) is removed from candidate set, no hard error (SFINAE) // * No specialization matches, take the primary template. int main() {}