Discussion
(The discussion is based on another answer that I will delete now.)
As in the original question, the following answer checks if it is possible to invoke an aggregate constructor with a given number of arguments. For aggregates, you can build a binary search on this template using the following properties from the standard:
8.5.1 (6):
The initialization list is poorly formed if the number of initializers-offers exceeds the number of elements or elements for initialization . [Example: char cv [4] = {a, s, d, f, 0}; // the error is poorly formed. - end example]
and
8.5.1 (7):
If there are fewer initializer elements in the list than there are members in the aggregate, then each member that is not explicitly initialized should be initialized from the default element initializer (9.2) or, if there is no default element initializer, from an empty list of initializers (8.5.4). [Example: struct S {int a; const char * b; int c; int d = b [a]; }; S ss = {1, "asdf"}; initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an expression of the form int {} (that is, 0) and ss.d with the value ss.b [ss.a] (i.e. s) and in struct X {int i, j, k = 42; }; X a [] = {1, 2, 3, 4, 5, 6}; X b [2] = {{1, 2, 3}, {4, 5, 6}}; a and b have the same meaning example]
However, as you already meant by the question heading, binary search does not work with non-aggregates at all, firstly, because they usually are not called with less parameters than necessary, and then because non-aggregates can have explicit constructors, so the "convert to something" trick through a struct filler will not work.
Implementation
The first ingredient is the is_callable check from here :
template<typename V, typename ... Args> struct is_constructible_impl { template<typename C> static constexpr auto test(int) -> decltype(C{std::declval<Args>() ...}, bool{}) { return true; } template<typename> static constexpr auto test(...) { return false; } static constexpr bool value = test<V>(int{}); using type = std::integral_constant<bool, value>; }; template<typename ... Args> using is_constructible = typename is_callable_impl<Args...>::type;
Please note that this parameter can also be used with fewer parameters than necessary (unlike your check).
Next is a helper function that takes an integer argument and returns whether the aggregate is callable with the appropriate number of constructor arguments:
template<typename A, size_t ... I> constexpr auto check_impl(std::index_sequence<I ...>) { return is_constructible<A, decltype(I, filler{}) ...>::value; } template<typename A, size_t N> constexpr auto check() { return check_impl<A>(std::make_index_sequence<N>{}); }
And finally, binary search:
template<typename A, size_t Low, size_t Up, size_t i = Low + (Up - Low)/2> struct binary_search : public std::conditional_t<check<A, i>() && !check<A,i+1>() , std::integral_constant<size_t, i> , std::conditional_t<check<A, i>() , binary_search<A, i, Up> , binary_search<A, Low, i> > > {};
Use it like
int main() { static_assert(binary_search<A2,0,10>::value==2); }
Live on coliru