Search code examples
c++templatesconstructorarity

c++ How to get arity of a constructor?


Given a template parameter class T that has a single constructor (no copy or move constructor either) and zero default arguments, is there some way to find the arity of T(...)?

My attempts so far

#include <iostream>
#include <string>
#include <vector>

template <typename F> struct function_arity;

template <typename R, typename... Args>
struct function_arity<R (Args...)>
    : std::integral_constant<std::size_t, sizeof...(Args)> {};

template <typename R, typename... Args>
struct function_arity<R (*)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename... Args>
struct function_arity<R (&)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...) const> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...)> : function_arity<R (Args...)> {};

template <typename C>
struct function_arity : function_arity<decltype(&C::operator())> {};

struct no_copy { no_copy() = default; no_copy(const no_copy&) = delete; };
struct no_move { no_move() = default; no_move(no_move&&) = delete; };

struct A : no_copy, no_move { A(int, float) { std::cout << "A!\n"; }; };
struct B : no_copy, no_move { B(double) { std::cout << "B!\n"; }; };
struct C : no_copy, no_move { C() { std::cout << "C!\n"; }; };

int main()
{
    std::cout << function_arity<&A::A>::value << "\n";
    return 0;
}

Solution

  • If we make the following assumptions:

    • The parameters are either fixed types or (entirely unrestricted!) catch-all parameters (not e.g. std::basic_string<CharT>)
    • The parameters are MoveConstructible

    , then

    #include <type_traits>
    #include <utility>
    
    namespace detail {
        template <typename Ignore>
        struct anything {
            template <typename T,
                      typename=std::enable_if_t<not std::is_same<Ignore, std::decay_t<T>>{}>>
            operator T&&();
        };
    
        template <typename U, typename=void, typename... args>
        struct test : test<U, void, args..., anything<U>> {};
        template <typename U, typename... args>
        struct test<U, std::enable_if_t<std::is_constructible<U, args...>{}
                                     && sizeof...(args) < 32>, args...>
            : std::integral_constant<std::size_t, sizeof...(args)> {};
        template <typename U, typename... args>
        struct test<U, std::enable_if_t<sizeof...(args) == 32>, args...>
            : std::integral_constant<std::size_t, (std::size_t)-1> {};
    }
    
    template <typename U>
    using ctor_arity = detail::test<U, void>;
    

    …should work as expected. Demo.
    Note that the above approach is easily translatable into C++11.